/* Plugin of Miranda IM for communicating with users of the MSN Messenger protocol. Copyright (c) 2012-2014 Miranda NG Team Copyright (c) 2006-2012 Boris Krasnovskiy. Copyright (c) 2003-2005 George Hazan. Copyright (c) 2002-2003 Richard Hughes (original version). This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "msn_global.h" #include "msn_proto.h" #include "m_smileyadd.h" static const char sttP2Pheader[] = "Content-Type: application/x-msnmsgrp2p\r\n" "P2P-Dest: %s\r\n\r\n"; static const char sttP2PheaderV2[] = "Content-Type: application/x-msnmsgrp2p\r\n" "P2P-Dest: %s\r\n" "P2P-Src: %s;%s\r\n\r\n"; const char sttVoidUid[] = "{00000000-0000-0000-0000-000000000000}"; static const char szUbnCall[] = "{F13B5C79-0126-458F-A29D-747C79C56530}"; static const char p2pV2Caps[] = { 0x01, 0x0C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x0F, 0x01, 0x00, 0x00 }; void P2P_Header::logHeader(CMsnProto *ppro) { ppro->debugLogA("--- Printing message header"); ppro->debugLogA(" SessionID = %08X", mSessionID); ppro->debugLogA(" MessageID = %08X", mID); #ifndef __GNUC__ ppro->debugLogA(" Offset of data = %I64u", mOffset); ppro->debugLogA(" Total amount of data = %I64u", mTotalSize); #else ppro->debugLogA(" Offset of data = %llu", mOffset); ppro->debugLogA(" Total amount of data = %llu", hdrdata->mTotalSize); #endif ppro->debugLogA(" Data in packet = %lu bytes", mPacketLen); ppro->debugLogA(" Flags = %08X", mFlags); ppro->debugLogA(" Acknowledged session ID: %08X", mAckSessionID); ppro->debugLogA(" Acknowledged message ID: %08X", mAckUniqueID); #ifndef __GNUC__ ppro->debugLogA(" Acknowledged data size: %I64u", mAckDataSize); #else ppro->debugLogA(" Acknowledged data size: %llu", mAckDataSize); #endif ppro->debugLogA("------------------------"); } void P2PV2_Header::logHeader(CMsnProto *ppro) { ppro->debugLogA("--- Printing message header"); ppro->debugLogA(" SessionID = %08X", mSessionID); ppro->debugLogA(" MessageID = %08X", mID); #ifndef __GNUC__ ppro->debugLogA(" Remaining amount of data = %I64u", mRemSize); #else ppro->debugLogA(" Remaining amount of data = %llu", mTotalSize); #endif ppro->debugLogA(" Data in packet = %lu bytes", mPacketLen); ppro->debugLogA(" Packet Number = %lu", mPacketNum); ppro->debugLogA(" Operation Code = %08X", mOpCode); ppro->debugLogA(" TF Code = %08X", mTFCode); ppro->debugLogA(" Acknowledged message ID: %08X", mAckUniqueID); ppro->debugLogA("------------------------"); } bool CMsnProto::p2p_createListener(filetransfer* ft, directconnection *dc, MimeHeaders& chdrs) { if (MyConnection.extIP == 0) return false; NETLIBBIND nlb = {0}; nlb.cbSize = sizeof(nlb); nlb.pfnNewConnectionV2 = MSN_ConnectionProc; nlb.pExtra = this; HANDLE sb = (HANDLE) CallService(MS_NETLIB_BINDPORT, (WPARAM) m_hNetlibUser, (LPARAM)&nlb); if (sb == NULL) { debugLogA("Unable to bind the port for incoming transfers"); return false; } ThreadData* newThread = new ThreadData; newThread->mType = SERVER_P2P_DIRECT; newThread->mCaller = 3; newThread->mIncomingBoundPort = sb; newThread->mIncomingPort = nlb.wPort; strncpy(newThread->mCookie, dc->callId , sizeof(newThread->mCookie)); newThread->mInitialContactWLID = mir_strdup(ft->p2p_dest); newThread->startThread(&CMsnProto::p2p_filePassiveThread, this); char szIpv4[256] = ""; char szIpv6[256] = ""; const char *szExtIp = MyConnection.GetMyExtIPStr(); bool ipInt = false; int i4 = 0, i6 = 0; NETLIBIPLIST* ihaddr = (NETLIBIPLIST*)CallService(MS_NETLIB_GETMYIP, 1, 0); for (unsigned i = 0; i < ihaddr->cbNum; ++i) { if (strchr(ihaddr->szIp[i], ':')) { if (i6++ != 0) strcat(szIpv6, " "); strcat(szIpv6, ihaddr->szIp[i]); } else { if (i4++ != 0) strcat(szIpv4, " "); ipInt |= (strcmp(ihaddr->szIp[i], szExtIp) == 0); strcat(szIpv4, ihaddr->szIp[i]); } } mir_free(ihaddr); chdrs.addString("Bridge", "TCPv1"); chdrs.addBool("Listening", true); if (dc->useHashedNonce) chdrs.addString("Hashed-Nonce", dc->mNonceToHash(), 2); else chdrs.addString("Nonce", dc->mNonceToText(), 2); bool bUbnCall = !ft->p2p_sessionid; if (!ipInt) { chdrs.addString("IPv4External-Addrs", mir_strdup(MyConnection.GetMyExtIPStr()), bUbnCall ? 6 : 2); chdrs.addLong("IPv4External-Port", nlb.wExPort, bUbnCall ? 4 : 0); } chdrs.addString("IPv4Internal-Addrs", mir_strdup(szIpv4), bUbnCall ? 6 : 2); chdrs.addLong("IPv4Internal-Port", nlb.wPort, bUbnCall ? 4 : 0); if (szIpv6[0]) { chdrs.addString("IPv6-Addrs", mir_strdup(szIpv6), 2); chdrs.addLong("IPv6-Port", nlb.wPort); } chdrs.addULong("SessionID", ft->p2p_sessionid); chdrs.addString("SChannelState", "0"); chdrs.addString("Capabilities-Flags", "1"); return true; } bool p2p_IsDlFileOk(filetransfer* ft) { mir_sha1_ctx sha1ctx; BYTE sha[MIR_SHA1_HASH_SIZE]; mir_sha1_init(&sha1ctx); bool res = false; int fileId = _topen(ft->std.tszCurrentFile, O_RDONLY | _O_BINARY, _S_IREAD); if (fileId != -1) { BYTE buf[4096]; int bytes; while((bytes = _read(fileId, buf, sizeof(buf))) > 0) mir_sha1_append(&sha1ctx, buf, bytes); _close(fileId); mir_sha1_finish(&sha1ctx, sha); char *szSha = arrayToHex(sha, MIR_SHA1_HASH_SIZE); char *szAvatarHash = MSN_GetAvatarHash(ft->p2p_object); res = szAvatarHash != NULL && _stricmp(szAvatarHash, szSha) == 0; mir_free(szSha); mir_free(szAvatarHash); } return res; } ///////////////////////////////////////////////////////////////////////////////////////// // sttSavePicture2disk - final handler for avatars downloading void CMsnProto::p2p_pictureTransferFailed(filetransfer* ft) { switch(ft->p2p_type) { case MSN_APPID_AVATAR: case MSN_APPID_AVATAR2: { PROTO_AVATAR_INFORMATIONT AI = {0}; AI.cbSize = sizeof(AI); AI.hContact = ft->std.hContact; delSetting(ft->std.hContact, "AvatarHash"); ProtoBroadcastAck(AI.hContact, ACKTYPE_AVATAR, ACKRESULT_FAILED, &AI, 0); } break; } _tremove(ft->std.tszCurrentFile); } void CMsnProto::p2p_savePicture2disk(filetransfer* ft) { ft->close(); if (p2p_IsDlFileOk(ft)) { int fileId = _topen(ft->std.tszCurrentFile, O_RDONLY | _O_BINARY, _S_IREAD); if (fileId == -1) { p2p_pictureTransferFailed(ft); return; } const TCHAR* ext; int format; BYTE buf[6]; int bytes = _read(fileId, buf, sizeof(buf)); _close(fileId); if (bytes > 4) format = ProtoGetBufferFormat(buf, &ext); else { p2p_pictureTransferFailed(ft); return; } switch(ft->p2p_type) { case MSN_APPID_AVATAR: case MSN_APPID_AVATAR2: { PROTO_AVATAR_INFORMATIONT AI = {0}; AI.cbSize = sizeof(AI); AI.format = format; AI.hContact = ft->std.hContact; MSN_GetAvatarFileName(AI.hContact, AI.filename, SIZEOF(AI.filename), ext); _trename(ft->std.tszCurrentFile, AI.filename); // Store also avatar hash char *szAvatarHash = MSN_GetAvatarHash(ft->p2p_object); setString(ft->std.hContact, "AvatarSavedHash", szAvatarHash); mir_free(szAvatarHash); setString(ft->std.hContact, "PictSavedContext", ft->p2p_object); ProtoBroadcastAck(AI.hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &AI, 0); char *filename = mir_utf8encodeT(AI.filename); debugLogA("Avatar for contact %08x saved to file '%s'", AI.hContact, filename); mir_free(filename); } break; case MSN_APPID_CUSTOMSMILEY: case MSN_APPID_CUSTOMANIMATEDSMILEY: { SMADD_CONT cont; cont.cbSize = sizeof(SMADD_CONT); cont.hContact = ft->std.hContact; cont.type = 1; TCHAR* pathcpy = mir_tstrdup(ft->std.tszCurrentFile); _tcscpy(_tcsrchr(pathcpy, '.') + 1, ext); _trename(ft->std.tszCurrentFile, pathcpy); cont.path = pathcpy; CallService(MS_SMILEYADD_LOADCONTACTSMILEYS, 0, (LPARAM)&cont); mir_free(pathcpy); } break; } } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendAck - sends MSN P2P acknowledgement to the received message static const char sttVoidSession[] = "ACHTUNG!!! an attempt made to send a message via the empty session"; void CMsnProto::p2p_sendMsg(const char *wlid, unsigned appId, P2PB_Header& hdrdata, char* msgbody, size_t msgsz) { ThreadData* info = MSN_GetP2PThreadByContact(wlid); if (info == NULL) { bool isOffline; info = MSN_StartSB(wlid, isOffline); } p2p_sendMsg(info, wlid, appId, hdrdata, msgbody, msgsz); } void CMsnProto::p2p_sendMsg(ThreadData* info, const char *wlid, unsigned appId, P2PB_Header& hdrdata, char* msgbody, size_t msgsz) { unsigned msgType; if (info == NULL) msgType = 0; else if (info->mType == SERVER_P2P_DIRECT) msgType = 1; else msgType = 2; unsigned fportion = msgType == 1 ? 1352 : 1202; if (hdrdata.isV2Hdr()) fportion += 4; char* buf = (char*) alloca(sizeof(sttP2PheaderV2)+ MSN_MAX_EMAIL_LEN + 120 + fportion); size_t offset = 0; do { size_t portion = msgsz - offset; if (portion > fportion) portion = fportion; char* p = buf; // add message header p += msgType == 1 ? sizeof(unsigned) : sprintf(p, hdrdata.isV2Hdr() ? sttP2PheaderV2 : sttP2Pheader, wlid, MyOptions.szEmail, MyOptions.szMachineGuidP2P); //!!!!!!!!!!! if (hdrdata.isV2Hdr()) { P2PV2_Header *ph = (P2PV2_Header*)&hdrdata; if (offset == 0) { if (!info || !info->mBridgeInit) { if (info && ph->mSessionID) { P2PV2_Header tHdr; tHdr.mID = ph->mID; p2p_sendMsg(info, wlid, 0, tHdr, NULL, 0); } else { ph->mOpCode |= ph->mAckUniqueID && msgType != 1 ? 1 : 3; ph->mCap = p2pV2Caps; if (info) info->mBridgeInit = true; } } } else { ph->mOpCode = 0; ph->mCap = NULL; } } if (msgsz) { if (hdrdata.isV2Hdr()) { P2PV2_Header *ph = (P2PV2_Header*)&hdrdata; ph->mPacketLen = (unsigned)portion; ph->mRemSize = msgsz - offset - portion; ph->mTFCode = offset ? ph->mTFCode & 0xfe : ph->mTFCode | 0x01; if (offset == 0) ph->mPacketNum = p2p_getPktNum(wlid); } else { P2P_Header *ph = (P2P_Header*)&hdrdata; ph->mPacketLen = (unsigned)portion; ph->mOffset = offset; ph->mTotalSize = msgsz; } } // add message body p = hdrdata.createMsg(p, wlid, this); hdrdata.logHeader(this); if (msgsz) memcpy(p, msgbody + offset, portion); p += portion; // add message footer if (msgType != 1) { *(unsigned*)p = _htonl(appId); p += 4; } char* szEmail; switch (msgType) { case 0: parseWLID(NEWSTR_ALLOCA(wlid), NULL, &szEmail, NULL); MsgQueue_Add(szEmail, 'D', buf, p - buf); break; case 1: *(unsigned*)buf = (unsigned)(p - buf - sizeof(unsigned)); info->send(buf, p - buf); break; case 2: info->sendRawMessage('D', buf, p - buf); break; } offset += portion; } while (offset < msgsz); } void CMsnProto::p2p_sendAck(const char *wlid, P2PB_Header* hdr) { if (hdr == NULL) return; if (!hdr->isV2Hdr()) { P2P_Header *hdrdata = (P2P_Header*)hdr; P2P_Header tHdr; tHdr.mSessionID = hdrdata->mSessionID; tHdr.mAckDataSize = hdrdata->mTotalSize; tHdr.mFlags = 2; tHdr.mAckSessionID = hdrdata->mID; tHdr.mAckUniqueID = hdrdata->mAckSessionID; p2p_sendMsg(wlid, 0, tHdr, NULL, 0); } else { P2PV2_Header *hdrdata = (P2PV2_Header*)hdr; P2PV2_Header tHdr; tHdr.mAckUniqueID = hdrdata->mID; p2p_sendMsg(wlid, 0, tHdr, NULL, 0); } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendEndSession - sends MSN P2P file transfer end packet void CMsnProto::p2p_sendAbortSession(filetransfer* ft) { if (ft == NULL) { debugLogA(sttVoidSession); return; } if (ft->p2p_isV2) return; P2P_Header tHdr; tHdr.mSessionID = ft->p2p_sessionid; tHdr.mAckSessionID = ft->p2p_sendmsgid; tHdr.mID = p2p_getMsgId(ft->p2p_dest, 1); if (ft->std.flags & PFTS_SENDING) { tHdr.mFlags = 0x40; tHdr.mAckSessionID = tHdr.mID - 2; } else { tHdr.mAckUniqueID = 0x8200000f; tHdr.mFlags = 0x80; tHdr.mAckDataSize = ft->std.currentFileSize; } p2p_sendMsg(ft->p2p_dest, 0, tHdr, NULL, 0); ft->ts = time(NULL); } void CMsnProto::p2p_sendRedirect(filetransfer* ft) { if (ft == NULL) { debugLogA(sttVoidSession); return; } if (ft->p2p_isV2) return; P2P_Header tHdr; tHdr.mSessionID = ft->p2p_sessionid; tHdr.mFlags = 0x01; tHdr.mAckSessionID = ft->p2p_sendmsgid; tHdr.mAckDataSize = ft->std.currentFileProgress; p2p_sendMsg(ft->p2p_dest, 0, tHdr, NULL, 0); ft->tTypeReq = MSN_GetP2PThreadByContact(ft->p2p_dest) ? SERVER_P2P_DIRECT : SERVER_SWITCHBOARD; ft->ts = time(NULL); ft->p2p_waitack = true; } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendSlp - send MSN P2P SLP packet void CMsnProto::p2p_sendSlp(int iKind, filetransfer *ft, MimeHeaders &pHeaders, MimeHeaders &pContent, const char *wlid) { if (ft == NULL) { debugLogA(sttVoidSession); return; } if (wlid == NULL) wlid = ft->p2p_dest; size_t cbContLen = pContent.getLength(); pHeaders.addULong("Content-Length", (unsigned)cbContLen + 1); char* buf = (char*)alloca(pHeaders.getLength() + cbContLen + 512); char* p = buf; switch (iKind) { case -3: p += sprintf(p, "ACK MSNMSGR:%s MSNSLP/1.0", wlid); break; //!!!!!!!!!!!!!!!!!! case -2: p += sprintf(p, "INVITE MSNMSGR:%s MSNSLP/1.0", wlid); break; //!!!!!!!!!!!!!!!!!! case -1: p += sprintf(p, "BYE MSNMSGR:%s MSNSLP/1.0", wlid); break; //!!!!!!!!!!!!!!!!!! case 200: p += sprintf(p, "MSNSLP/1.0 200 OK"); break; //!!!!!!!!!!!!!!!!!! case 481: p += sprintf(p, "MSNSLP/1.0 481 No Such Call"); break; //!!!!!!!!!!!!!!!!!! case 500: p += sprintf(p, "MSNSLP/1.0 500 Internal Error"); break; //!!!!!!!!!!!!!!!!!! case 603: p += sprintf(p, "MSNSLP/1.0 603 DECLINE"); break; //!!!!!!!!!!!!!!!!!! case 1603: p += sprintf(p, "MSNSLP/1.0 603 Decline"); break; //!!!!!!!!!!!!!!!!!! default: return; } if (iKind < 0) { mir_free(ft->p2p_branch); ft->p2p_branch = getNewUuid(); } if (ft->p2p_isV2) { p += sprintf(p, "\r\nTo: \r\n" "From: \r\n" "Via: MSNSLP/1.0/TLP ;branch=%s\r\n", wlid, MyOptions.szEmail, MyOptions.szMachineGuidP2P, ft->p2p_branch); //!!!!!!!!!!!!!!!!!! } else { p += sprintf(p, "\r\nTo: \r\n" "From: \r\n" "Via: MSNSLP/1.0/TLP ;branch=%s\r\n", wlid, MyOptions.szEmail, ft->p2p_branch); //!!!!!!!!!!!!!!!!!! } p = pHeaders.writeToBuffer(p); p = pContent.writeToBuffer(p); unsigned short status = getWord(ft->std.hContact, "Status", ID_STATUS_OFFLINE); if (!(myFlags & cap_SupportsP2PBootstrap) || ft->p2p_sessionid || MSN_GetThreadByContact(wlid, SERVER_P2P_DIRECT) || status == ID_STATUS_OFFLINE || status == ID_STATUS_INVISIBLE || m_iStatus == ID_STATUS_INVISIBLE) { if (!ft->p2p_isV2) { P2P_Header tHdr; tHdr.mAckSessionID = ft->p2p_acksessid; p2p_sendMsg(wlid, 0, tHdr, buf, p - buf + 1); ft->p2p_waitack = true; switch (iKind) { case -1: case 500: case 603: ft->p2p_byemsgid = tHdr.mID; break; } } else { P2PV2_Header tHdr; tHdr.mTFCode = 0x01; p2p_sendMsg(wlid, 0, tHdr, buf, p - buf + 1); } } else msnNsThread->sendPacket("UUN", "%s 3 %d\r\n%s", wlid, p - buf, buf); ft->ts = time(NULL); } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendBye - closes P2P session void CMsnProto::p2p_sendBye(filetransfer* ft) { if (ft == NULL) { debugLogA(sttVoidSession); return; } MimeHeaders tHeaders(8); tHeaders.addString("CSeq", "0 "); tHeaders.addString("Call-ID", ft->p2p_callID); tHeaders.addLong("Max-Forwards", 0); tHeaders.addString("Content-Type", "application/x-msnmsgr-sessionclosebody"); MimeHeaders chdrs(2); chdrs.addULong("SessionID", ft->p2p_sessionid); chdrs.addString("SChannelState", "0"); p2p_sendSlp(-1, ft, tHeaders, chdrs); } void CMsnProto::p2p_sendCancel(filetransfer* ft) { p2p_sendBye(ft); p2p_sendAbortSession(ft); } void CMsnProto::p2p_sendNoCall(filetransfer* ft) { if (ft == NULL) { debugLogA(sttVoidSession); return; } MimeHeaders tHeaders(8); tHeaders.addString("CSeq", "0 "); tHeaders.addString("Call-ID", ft->p2p_callID); tHeaders.addLong("Max-Forwards", 0); tHeaders.addString("Content-Type", "application/x-msnmsgr-session-failure-respbody"); MimeHeaders chdrs(2); chdrs.addULong("SessionID", ft->p2p_sessionid); chdrs.addString("SChannelState", "0"); p2p_sendSlp(481, ft, tHeaders, chdrs); } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendStatus - send MSN P2P status and its description void CMsnProto::p2p_sendStatus(filetransfer* ft, long lStatus) { if (ft == NULL) { debugLogA(sttVoidSession); return; } MimeHeaders tHeaders(8); tHeaders.addString("CSeq", "1 "); tHeaders.addString("Call-ID", ft->p2p_callID); tHeaders.addLong("Max-Forwards", 0); MimeHeaders chdrs(2); chdrs.addULong("SessionID", ft->p2p_sessionid); if (lStatus != 1603) { tHeaders.addString("Content-Type", "application/x-msnmsgr-sessionreqbody"); chdrs.addString("SChannelState", "0"); } else tHeaders.addString("Content-Type", "application/x-msnmsgr-transrespbody"); p2p_sendSlp(lStatus, ft, tHeaders, chdrs); } void CMsnProto::p2p_sendAvatarInit(filetransfer* ft) { unsigned body = 0; if (ft->p2p_isV2) { P2PV2_Header tHdr; tHdr.mSessionID = ft->p2p_sessionid; tHdr.mTFCode = 0x01; p2p_sendMsg(ft->p2p_dest, ft->p2p_appID, tHdr, (char*)&body, sizeof(body)); } else { P2P_Header tHdr; tHdr.mSessionID = ft->p2p_sessionid; tHdr.mAckSessionID = ft->p2p_acksessid; p2p_sendMsg(ft->p2p_dest, ft->p2p_appID, tHdr, (char*)&body, sizeof(body)); ft->ts = time(NULL); ft->p2p_waitack = true; } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_connectTo - connects to a remote P2P server static const char p2p_greeting[8] = { 4, 0, 0, 0, 'f', 'o', 'o', 0 }; static void sttSendPacket(ThreadData* T, void* hdr, unsigned len) { T->send((char*)&len, sizeof(unsigned)); T->send((char*)hdr, len); } bool CMsnProto::p2p_connectTo(ThreadData* info, directconnection *dc) { NETLIBOPENCONNECTION tConn = {0}; tConn.cbSize = sizeof(tConn); tConn.szHost = info->mServer; tConn.flags = NLOCF_V2; tConn.timeout = 5; char* tPortDelim = strrchr(info->mServer, ':'); if (tPortDelim != NULL) { *tPortDelim = '\0'; tConn.wPort = (WORD)atol(tPortDelim + 1); } debugLogA("Connecting to %s:%d", tConn.szHost, tConn.wPort); info->s = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)m_hNetlibUser, (LPARAM)&tConn); if (info->s == NULL) { TWinErrorCode err; debugLogA("Connection Failed (%d): %s", err.mErrorCode, err.getText()); return false; } info->send(p2p_greeting, sizeof(p2p_greeting)); bool isV2 = strchr(info->mInitialContactWLID, ';') != NULL; P2P_Header reply; if (!isV2) { reply.mFlags = 0x100; if (dc->useHashedNonce) memcpy(&reply.mAckSessionID, dc->mNonce, sizeof(UUID)); else dc->xNonceToBin((UUID*)&reply.mAckSessionID); char buf[48]; reply.createMsg(buf, info->mInitialContactWLID, this); sttSendPacket(info, buf, sizeof(buf)); } else sttSendPacket(info, dc->mNonce, sizeof(UUID)); long cbPacketLen; HReadBuffer buf(info, 0); BYTE* p; if ((p = buf.surelyRead(4)) == NULL) { debugLogA("Error reading data, closing filetransfer"); return false; } cbPacketLen = *(long*)p; if ((p = buf.surelyRead(cbPacketLen)) == NULL) return false; bool cookieMatch; if (!isV2) { P2P_Header cookie((char*)p); if (dc->useHashedNonce) { char* hnonce = dc->calcHashedNonce((UUID*)&cookie.mAckSessionID); cookieMatch = strcmp(hnonce, dc->xNonce) == 0; mir_free(hnonce); } else cookieMatch = memcmp(&cookie.mAckSessionID, &reply.mAckSessionID, sizeof(UUID)) == 0; } else { char* hnonce = dc->calcHashedNonce((UUID*)p); cookieMatch = strcmp(hnonce, dc->xNonce) == 0; mir_free(hnonce); } if (!cookieMatch) { debugLogA("Invalid cookie received, exiting"); return false; } return true; } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_listen - acts like a local P2P server bool CMsnProto::p2p_listen(ThreadData* info, directconnection *dc) { switch(WaitForSingleObject(info->hWaitEvent, 10000)) { case WAIT_TIMEOUT: case WAIT_FAILED: debugLogA("Incoming connection timed out, closing file transfer"); MSN_StartP2PTransferByContact(info->mInitialContactWLID); LBL_Error: debugLogA("File listen failed"); return false; } HReadBuffer buf(info, 0); BYTE* p; if ((p = buf.surelyRead(8)) == NULL) goto LBL_Error; if (memcmp(p, p2p_greeting, 8) != 0) { debugLogA("Invalid input data, exiting"); return false; } if ((p = buf.surelyRead(4)) == NULL) { debugLogA("Error reading data, closing filetransfer"); return false; } long cbPacketLen = *(long*)p; if ((p = buf.surelyRead(cbPacketLen)) == NULL) goto LBL_Error; bool cookieMatch; bool isV2 = strchr(info->mInitialContactWLID, ';') != NULL; if (!isV2) { P2P_Header cookie((char*)p); if (dc->useHashedNonce) { char* hnonce = dc->calcHashedNonce((UUID*)&cookie.mAckSessionID); cookieMatch = strcmp(hnonce, dc->xNonce) == 0; mir_free(hnonce); memcpy(&cookie.mAckSessionID, dc->mNonce, sizeof(UUID)); } else cookieMatch = memcmp(&cookie.mAckSessionID, dc->mNonce, sizeof(UUID)) == 0; if (!cookieMatch) { debugLogA("Invalid cookie received, exiting"); return false; } char buf[48]; cookie.createMsg(buf, info->mInitialContactWLID, this); sttSendPacket(info, buf, sizeof(buf)); } else { char* hnonce = dc->calcHashedNonce((UUID*)p); cookieMatch = strcmp(hnonce, dc->xNonce) == 0; mir_free(hnonce); if (!cookieMatch) { debugLogA("Invalid cookie received, exiting"); goto LBL_Error; } sttSendPacket(info, dc->mNonce, sizeof(UUID)); } return true; } LONG CMsnProto::p2p_sendPortion(filetransfer* ft, ThreadData* T, bool isV2) { LONG trid; char databuf[1500], *p = databuf; // Compute the amount of data to send unsigned fportion = T->mType == SERVER_P2P_DIRECT ? 1352 : 1202; if (isV2) fportion += 4; const unsigned __int64 dt = ft->std.currentFileSize - ft->std.currentFileProgress; const unsigned portion = dt > fportion ? fportion : dt; // Fill data size for direct transfer if (T->mType != SERVER_P2P_DIRECT) p += sprintf(p, isV2 ? sttP2PheaderV2 : sttP2Pheader, ft->p2p_dest, MyOptions.szEmail, MyOptions.szMachineGuidP2P); //!!!!!!!!!!!!!!!!!! else p += sizeof(unsigned); if (!isV2) { // Fill P2P header P2P_Header H; H.mSessionID = ft->p2p_sessionid; H.mID = ft->p2p_sendmsgid; H.mFlags = ft->p2p_appID == MSN_APPID_FILE ? 0x01000030 : 0x20; H.mTotalSize = ft->std.currentFileSize; H.mOffset = ft->std.currentFileProgress; H.mPacketLen = portion; H.mAckSessionID = ft->p2p_acksessid; p = H.createMsg(p, ft->p2p_dest, this); } else { P2PV2_Header H; H.mSessionID = ft->p2p_sessionid; H.mTFCode = (ft->p2p_appID == MSN_APPID_FILE ? 6 : 4) | (ft->std.currentFileProgress ? 0 : 1); H.mRemSize = ft->std.currentFileSize - ft->std.currentFileProgress - portion; H.mPacketLen = portion; H.mPacketNum = ft->p2p_sendmsgid; p = H.createMsg(p, ft->p2p_dest, this); H.logHeader(this); } if (T->mType == SERVER_P2P_DIRECT) *(unsigned*)databuf = portion + (p - databuf) - (unsigned)sizeof(unsigned); // Fill data (payload) for transfer if (ft->fileId == -1) return 0; _read(ft->fileId, p, portion); p += portion; if (T->mType == SERVER_P2P_DIRECT) trid = T->send(databuf, p - databuf); else { // Define packet footer for server transfer *(unsigned*)p = _htonl(ft->p2p_appID); p += sizeof(unsigned); trid = T->sendRawMessage('D', (char *)databuf, p - databuf); } if (trid != 0) { ft->std.totalProgress += portion; ft->std.currentFileProgress += portion; if (ft->p2p_appID == MSN_APPID_FILE && clock() >= ft->nNotify) { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); ft->nNotify = clock() + 500; } } else debugLogA(" Error sending"); ft->ts = time(NULL); ft->p2p_waitack = true; return trid; } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendFeedThread - sends a file via server void __cdecl CMsnProto::p2p_sendFeedThread(void* arg) { ThreadData* info = (ThreadData*)arg; bool isV2 = strchr(info->mInitialContactWLID, ';') != NULL; info->contactJoined(info->mInitialContactWLID); mir_free(info->mInitialContactWLID); info->mInitialContactWLID = NULL; debugLogA("File send thread started"); switch(WaitForSingleObject(info->hWaitEvent, 6000)) { case WAIT_FAILED: debugLogA("File send wait failed"); return; } HANDLE hLockHandle = NULL; ThreadData* T = NULL; TInfoType lastType = SERVER_NOTIFICATION; filetransfer *ft = p2p_getSessionByCallID(info->mCookie, info->mJoinedIdentContactsWLID.getCount() ? info->mJoinedIdentContactsWLID[0] : info->mJoinedContactsWLID[0]); if (ft != NULL && WaitForSingleObject(ft->hLockHandle, 2000) == WAIT_OBJECT_0) { hLockHandle = ft->hLockHandle; if (isV2) ft->p2p_sendmsgid = p2p_getPktNum(ft->p2p_dest); else { if (ft->p2p_sendmsgid == 0) ft->p2p_sendmsgid = p2p_getMsgId(ft->p2p_dest, 1); } T = MSN_GetP2PThreadByContact(ft->p2p_dest); if (T != NULL) ft->tType = lastType = T->mType; ReleaseMutex(hLockHandle); } else return; bool fault = false; while (WaitForSingleObject(hLockHandle, 2000) == WAIT_OBJECT_0 && ft->std.currentFileProgress < ft->std.currentFileSize && !ft->bCanceled) { if (ft->tType != lastType) T = MSN_GetThreadByContact(ft->p2p_dest, ft->tType); if (ft->bCanceled) break; bool cfault = (T == NULL || p2p_sendPortion(ft, T, isV2) == 0); if (cfault) { if (fault) { debugLogA("File send failed"); break; } else SleepEx(3000, TRUE); // Allow 3 sec for redirect request } fault = cfault; ReleaseMutex(hLockHandle); if (T != NULL && T->mType != SERVER_P2P_DIRECT) WaitForSingleObject(T->hWaitEvent, 5000); } ReleaseMutex(hLockHandle); if (ft->p2p_appID == MSN_APPID_FILE) ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); if (isV2) { if (!ft->bCanceled) { ft->bCompleted = true; p2p_sendBye(ft); } p2p_sessionComplete(ft); } debugLogA("File send thread completed"); } void CMsnProto::p2p_sendFeedStart(filetransfer* ft) { if (ft->std.flags & PFTS_SENDING) { ThreadData* newThread = new ThreadData; newThread->mType = SERVER_FILETRANS; strcpy(newThread->mCookie, ft->p2p_callID); newThread->mInitialContactWLID = mir_strdup(ft->p2p_dest); newThread->startThread(&CMsnProto::p2p_sendFeedThread, this); } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_sendFileDirectly - sends a file via MSN P2P protocol void CMsnProto::p2p_sendRecvFileDirectly(ThreadData* info) { long cbPacketLen = 0; long state = 0; HReadBuffer buf(info, 0); char *wlid = info->mInitialContactWLID; info->contactJoined(wlid); info->mInitialContactWLID = NULL; MSN_StartP2PTransferByContact(wlid); p2p_redirectSessions(wlid); p2p_startSessions(wlid); bool isV2 = strchr(wlid, ';') != NULL; for (;;) { long len = state ? cbPacketLen : 4; BYTE* p = buf.surelyRead(len); if (p == NULL) break; if (state == 0) cbPacketLen = *(long*)p; else if (!isV2) p2p_processMsg(info, (char*)p, wlid); else p2p_processMsgV2(info, (char*)p, wlid); state = (state + 1) % 2; } info->contactLeft(wlid); p2p_redirectSessions(wlid); mir_free(wlid); } ///////////////////////////////////////////////////////////////////////////////////////// // bunch of thread functions to cover all variants of P2P file transfers void __cdecl CMsnProto::p2p_fileActiveThread(void* arg) { ThreadData* info = (ThreadData*)arg; debugLogA("p2p_fileActiveThread() started: connecting to '%s'", info->mServer); directconnection *dc = p2p_getDCByCallID(info->mCookie, info->mInitialContactWLID); if (dc) { if (p2p_connectTo(info, dc)) p2p_sendRecvFileDirectly(info); else { mir_free(info->mInitialContactWLID); info->mInitialContactWLID = NULL; } if (!MSN_GetThreadByContact(dc->wlid, SERVER_P2P_DIRECT) && !MSN_GetUnconnectedThread(dc->wlid, SERVER_P2P_DIRECT)) p2p_unregisterDC(dc); } debugLogA("p2p_fileActiveThread() completed: connecting to '%s'", info->mServer); } void __cdecl CMsnProto::p2p_filePassiveThread(void* arg) { ThreadData* info = (ThreadData*)arg; debugLogA("p2p_filePassiveThread() started: listening"); directconnection *dc = p2p_getDCByCallID(info->mCookie, info->mInitialContactWLID); if (dc) { if (p2p_listen(info, dc)) p2p_sendRecvFileDirectly(info); else { mir_free(info->mInitialContactWLID); info->mInitialContactWLID = NULL; } if (!MSN_GetThreadByContact(dc->wlid, SERVER_P2P_DIRECT) && !MSN_GetUnconnectedThread(dc->wlid, SERVER_P2P_DIRECT)) p2p_unregisterDC(dc); } debugLogA("p2p_filePassiveThread() completed"); } void CMsnProto::p2p_InitFileTransfer( ThreadData* info, MimeHeaders& tFileInfo, MimeHeaders& tFileInfo2, const char* wlid) { if (info->mJoinedContactsWLID.getCount() == 0 && info->mJoinedIdentContactsWLID.getCount() == 0) return; const char *szCallID = tFileInfo["Call-ID"], *szBranch = tFileInfo["Via"]; if (szBranch != NULL) { szBranch = strstr(szBranch, "branch="); if (szBranch != NULL) szBranch += 7; } if (szCallID == NULL || szBranch == NULL) { debugLogA("Ignoring invalid invitation: CallID='%s', szBranch='%s'", szCallID, szBranch); return; } const char *szSessionID = tFileInfo2["SessionID"], *szEufGuid = tFileInfo2["EUF-GUID"], *szContext = tFileInfo2["Context"], *szAppId = tFileInfo2["AppID"]; if (szSessionID == NULL || szAppId == NULL || szEufGuid == NULL) { debugLogA("Ignoring invalid invitation: SessionID='%s', AppID=%s, Branch='%s',Context='%s'", szSessionID, szAppId, szEufGuid, szContext); return; } unsigned dwAppID = strtoul(szAppId, NULL, 10); unsigned dwSessionId = strtoul(szSessionID, NULL, 10); if (p2p_getSessionByID(dwSessionId)) return; szContext = (char*)mir_base64_decode(szContext, 0); filetransfer* ft = new filetransfer(this); ft->p2p_acksessid = MSN_GenRandom(); ft->p2p_sessionid = dwSessionId; ft->p2p_appID = dwAppID == MSN_APPID_AVATAR ? MSN_APPID_AVATAR2 : dwAppID; ft->p2p_type = dwAppID; ft->p2p_ackID = dwAppID == MSN_APPID_FILE ? 2000 : 1000; replaceStr(ft->p2p_callID, szCallID); replaceStr(ft->p2p_branch, szBranch); ft->p2p_dest = mir_strdup(wlid); ft->p2p_isV2 = strchr(wlid, ';') != NULL; ft->std.hContact = MSN_HContactFromEmail(wlid); p2p_registerSession(ft); switch (dwAppID) { case MSN_APPID_AVATAR: case MSN_APPID_AVATAR2: if (!_stricmp(szEufGuid, "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}")) { DBVARIANT dbv; bool pictmatch = !getString("PictObject", &dbv); if (pictmatch) { UrlDecode(dbv.pszVal); ezxml_t xmlcon = ezxml_parse_str((char*)szContext, strlen(szContext)); ezxml_t xmldb = ezxml_parse_str(dbv.pszVal, strlen(dbv.pszVal)); const char *szCtBuf = ezxml_attr(xmlcon, "SHA1C"); if (szCtBuf) { const char *szPtBuf = ezxml_attr(xmldb, "SHA1C"); pictmatch = szPtBuf && strcmp(szCtBuf, szPtBuf) == 0; } else { const char *szCtBuf = ezxml_attr(xmlcon, "SHA1D"); const char *szPtBuf = ezxml_attr(xmldb, "SHA1D"); pictmatch = szCtBuf && szPtBuf && strcmp(szCtBuf, szPtBuf) == 0; } ezxml_free(xmlcon); ezxml_free(xmldb); db_free(&dbv); } if (pictmatch) { TCHAR szFileName[MAX_PATH]; MSN_GetAvatarFileName(NULL, szFileName, SIZEOF(szFileName), NULL); ft->fileId = _topen(szFileName, O_RDONLY | _O_BINARY, _S_IREAD); if (ft->fileId == -1) { p2p_sendStatus(ft, 603); MSN_ShowError("Your avatar not set correctly. Avatar should be set in View/Change My Details | Avatar"); debugLogA("Unable to open avatar file '%s', error %d", szFileName, errno); p2p_unregisterSession(ft); } else { mir_free(ft->std.tszCurrentFile); ft->std.tszCurrentFile = mir_tstrdup(szFileName); // debugLogA("My avatar file opened for %s as %08p::%d", szEmail, ft, ft->fileId); ft->std.totalBytes = ft->std.currentFileSize = _filelengthi64(ft->fileId); ft->std.flags |= PFTS_SENDING; //---- send 200 OK Message p2p_sendStatus(ft, 200); p2p_sendFeedStart(ft); if (ft->p2p_isV2) { p2p_sendAvatarInit(ft); MSN_StartP2PTransferByContact(ft->p2p_dest); } } } else { p2p_sendStatus(ft, 603); debugLogA("Requested avatar does not match current avatar"); p2p_unregisterSession(ft); } } break; case MSN_APPID_FILE: if (!_stricmp(szEufGuid, "{5D3E02AB-6190-11D3-BBBB-00C04F795683}")) { wchar_t* wszFileName = ((HFileContext*)szContext)->wszFileName; for (wchar_t* p = wszFileName; *p != 0; p++) { switch(*p) { case ':': case '?': case '/': case '\\': case '*': *p = '_'; } } mir_free(ft->std.tszCurrentFile); ft->std.tszCurrentFile = mir_u2t(wszFileName); ft->std.totalBytes = ft->std.currentFileSize = ((HFileContext*)szContext)->dwSize; ft->std.totalFiles = 1; TCHAR tComment[40]; mir_sntprintf(tComment, SIZEOF(tComment), TranslateT("%I64u bytes"), ft->std.currentFileSize); PROTORECVFILET pre = {0}; pre.flags = PREF_TCHAR; pre.fileCount = 1; pre.timestamp = time(NULL); pre.tszDescription = tComment; pre.ptszFiles = &ft->std.tszCurrentFile; pre.lParam = (LPARAM)ft; ProtoChainRecvFile(ft->std.hContact, &pre); } break; case MSN_APPID_WEBCAM: if (!_stricmp(szEufGuid, "{4BD96FC0-AB17-4425-A14A-439185962DC8}")) { MSN_ShowPopup(ft->std.hContact, TranslateT("Contact tried to send its webcam data (not currently supported)"), MSN_ALLOW_MSGBOX | MSN_SHOW_ERROR); } if (!_stricmp(szEufGuid, "{1C9AA97E-9C05-4583-A3BD-908A196F1E92}")) { MSN_ShowPopup(ft->std.hContact, TranslateT("Contact tried to view your webcam data (not currently supported)"), MSN_ALLOW_MSGBOX | MSN_SHOW_ERROR); } p2p_sendStatus(ft, 603); p2p_unregisterSession(ft); break; case MSN_APPID_MEDIA_SHARING: // MSN_ShowPopup(ft->std.hContact, // TranslateT("Contact tried to share media with us (not currently supported)"), // MSN_ALLOW_MSGBOX | MSN_SHOW_ERROR); p2p_sendStatus(ft, 603); p2p_unregisterSession(ft); break; default: p2p_sendStatus(ft, 603); p2p_unregisterSession(ft); debugLogA("Invalid or unknown data transfer request (AppID/EUF-GUID: %ld/%s)", dwAppID, szEufGuid); break; } mir_free((void*)szContext); } void CMsnProto::p2p_InitDirectTransfer(MimeHeaders& tFileInfo, MimeHeaders& tFileInfo2, const char* wlid) { const char *szCallID = tFileInfo["Call-ID"], *szBranch = tFileInfo["Via"], *szConnType = tFileInfo2["Conn-Type"], *szUPnPNat = tFileInfo2["UPnPNat"], *szNetID = tFileInfo2["NetID"], *szICF = tFileInfo2["ICF"], *szHashedNonce = tFileInfo2["Hashed-Nonce"]; if (szBranch != NULL) { szBranch = strstr(szBranch, "branch="); if (szBranch != NULL) szBranch += 7; } if (szCallID == NULL || szBranch == NULL) { debugLogA("Ignoring invalid invitation: CallID='%s', Branch='%s'", szCallID, szBranch); return; } if (szConnType == NULL || szUPnPNat == NULL || szICF == NULL || szNetID == NULL) { debugLogA("Ignoring invalid invitation: ConnType='%s', UPnPNat='%s', ICF='%s', NetID='%s'", szConnType, szUPnPNat, szICF, szNetID); return; } filetransfer ftl(this), *ft = p2p_getSessionByCallID(szCallID, wlid); if (!ft || !ft->p2p_sessionid) { ft = &ftl; replaceStr(ft->p2p_dest, wlid); replaceStr(ft->p2p_callID, szCallID); replaceStr(ft->p2p_branch, szBranch); ft->p2p_isV2 = strchr(wlid, ';') != NULL; ft->std.hContact = MSN_HContactFromEmail(wlid); } else { replaceStr(ft->p2p_callID, szCallID); replaceStr(ft->p2p_branch, szBranch); ft->p2p_acksessid = MSN_GenRandom(); /* if (p2p_isAvatarOnly(ft->std.hContact)) { p2p_sendStatus(ft, 1603); return; } else ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, ft, 0); */ } directconnection *dc = p2p_getDCByCallID(szCallID, wlid); if (dc) { if (MSN_GetThreadByContact(wlid, SERVER_P2P_DIRECT)) { p2p_sendStatus(ft, 1603); p2p_unregisterDC(dc); return; } p2p_unregisterDC(dc); } dc = new directconnection(szCallID, wlid); dc->useHashedNonce = szHashedNonce != NULL; if (dc->useHashedNonce) dc->xNonce = mir_strdup(szHashedNonce); p2p_registerDC(dc); MimeHeaders tResult(8); tResult.addString("CSeq", "1 "); tResult.addString("Call-ID", szCallID); tResult.addLong("Max-Forwards", 0); MyConnectionType conType = {0}; conType.extIP = atol(szNetID); conType.SetUdpCon(szConnType); conType.upnpNAT = strcmp(szUPnPNat, "true") == 0; conType.icf = strcmp(szICF, "true") == 0; conType.CalculateWeight(); MimeHeaders chdrs(12); bool listen = false; debugLogA("Connection weight, his: %d mine: %d", conType.weight, MyConnection.weight); if (conType.weight <= MyConnection.weight) listen = p2p_createListener(ft, dc, chdrs); if (!listen) { chdrs.addString("Bridge", "TCPv1"); chdrs.addBool("Listening", false); if (dc->useHashedNonce) chdrs.addString("Hashed-Nonce", dc->mNonceToHash(), 2); else chdrs.addString("Nonce", sttVoidUid); chdrs.addULong("SessionID", ft->p2p_sessionid); chdrs.addString("SChannelState", "0"); chdrs.addString("Capabilities-Flags", "1"); } tResult.addString("Content-Type", "application/x-msnmsgr-transrespbody"); if (!ft->p2p_isV2) p2p_getMsgId(ft->p2p_dest, -1); p2p_sendSlp(200, ft, tResult, chdrs); } void CMsnProto::p2p_startConnect(const char* wlid, const char* szCallID, const char* addr, const char* port, bool ipv6) { if (port == NULL) return; char *pPortTokBeg = (char*)port; for (;;) { char *pPortTokEnd = strchr(pPortTokBeg, ' '); if (pPortTokEnd != NULL) *pPortTokEnd = 0; char *pAddrTokBeg = (char*)addr; for (;;) { char *pAddrTokEnd = strchr(pAddrTokBeg, ' '); if (pAddrTokEnd != NULL) *pAddrTokEnd = 0; ThreadData* newThread = new ThreadData; newThread->mType = SERVER_P2P_DIRECT; newThread->mInitialContactWLID = mir_strdup(wlid); mir_snprintf(newThread->mCookie, sizeof(newThread->mCookie), "%s", szCallID); mir_snprintf(newThread->mServer, sizeof(newThread->mServer), ipv6 ? "[%s]:%s" : "%s:%s", pAddrTokBeg, pPortTokBeg); newThread->startThread(&CMsnProto::p2p_fileActiveThread, this); if (pAddrTokEnd == NULL) break; *pAddrTokEnd = ' '; pAddrTokBeg = pAddrTokEnd + 1; } if (pPortTokEnd == NULL) break; *pPortTokEnd = ' '; pPortTokBeg = pPortTokEnd + 1; } } void CMsnProto::p2p_InitDirectTransfer2(MimeHeaders& tFileInfo, MimeHeaders& tFileInfo2, const char* wlid) { const char *szCallID = tFileInfo["Call-ID"], *szInternalAddress = tFileInfo2["IPv4Internal-Addrs"], *szInternalPort = tFileInfo2["IPv4Internal-Port"], *szExternalAddress = tFileInfo2["IPv4External-Addrs"], *szExternalPort = tFileInfo2["IPv4External-Port"], *szNonce = tFileInfo2["Nonce"], *szHashedNonce = tFileInfo2["Hashed-Nonce"], *szListening = tFileInfo2["Listening"], *szV6Address = tFileInfo2["IPv6-Addrs"], *szV6Port = tFileInfo2["IPv6-Port" ]; if ((szNonce == NULL && szHashedNonce == NULL) || szListening == NULL) { debugLogA("Ignoring invalid invitation: Listening='%s', Nonce=%s", szListening, szNonce); return; } directconnection* dc = p2p_getDCByCallID(szCallID, wlid); if (dc == NULL) { dc = new directconnection(szCallID, wlid); p2p_registerDC(dc); } dc->useHashedNonce = szHashedNonce != NULL; replaceStr(dc->xNonce, szHashedNonce ? szHashedNonce : szNonce); if (!strcmp(szListening, "true") && strcmp(dc->xNonce, sttVoidUid)) { p2p_startConnect(wlid, szCallID, szV6Address, szV6Port, true); p2p_startConnect(wlid, szCallID, szInternalAddress, szInternalPort, false); p2p_startConnect(wlid, szCallID, szExternalAddress, szExternalPort, false); } } void CMsnProto::p2p_AcceptTransfer(MimeHeaders& tFileInfo, MimeHeaders& tFileInfo2, const char* wlid) { const char *szCallID = tFileInfo["Call-ID"]; const char* szOldContentType = tFileInfo["Content-Type"]; const char *szBranch = tFileInfo["Via"]; if (szBranch != NULL) { szBranch = strstr(szBranch, "branch="); if (szBranch != NULL) szBranch += 7; } filetransfer ftl(this), *ft = p2p_getSessionByCallID(szCallID, wlid); if (!ft || !ft->p2p_sessionid) { ft = &ftl; replaceStr(ft->p2p_branch, szBranch); replaceStr(ft->p2p_callID, szCallID); replaceStr(ft->p2p_dest, wlid); ft->p2p_isV2 = strchr(wlid, ';') != NULL; ft->std.hContact = MSN_HContactFromEmail(wlid); } else { if (!(ft->std.flags & PFTS_SENDING)) { replaceStr(ft->p2p_branch, szBranch); replaceStr(ft->p2p_callID, szCallID); } } if (szCallID == NULL || szBranch == NULL || szOldContentType == NULL) { debugLogA("Ignoring invalid invitation: CallID='%s', szBranch='%s'", szCallID, szBranch); LBL_Close: p2p_sendStatus(ft, 500); return; } MimeHeaders tResult(8); tResult.addString("CSeq", "0 "); tResult.addString("Call-ID", ft->p2p_callID); tResult.addLong("Max-Forwards", 0); MimeHeaders chdrs(12); if (!strcmp(szOldContentType, "application/x-msnmsgr-sessionreqbody")) { if (ft == &ftl) { p2p_sendCancel(ft); return; } if (!ft->bAccepted) { replaceStr(ft->p2p_dest, wlid); ft->bAccepted = true; } else return; if (ft->p2p_type != MSN_APPID_FILE) { if (ft->fileId == -1) ft->create(); return; } p2p_sendFeedStart(ft); ThreadData* T = MSN_GetP2PThreadByContact(ft->p2p_dest); if (T != NULL && T->mType == SERVER_P2P_DIRECT) { MSN_StartP2PTransferByContact(ft->p2p_dest); return; } if (usingGateway) MSN_StartP2PTransferByContact(ft->p2p_dest); directconnection* dc = new directconnection(szCallID, wlid); p2p_registerDC(dc); tResult.addString("Content-Type", "application/x-msnmsgr-transreqbody"); chdrs.addString("Bridges", "TCPv1"); chdrs.addLong("NetID", MyConnection.extIP); chdrs.addString("Conn-Type", MyConnection.GetMyUdpConStr()); chdrs.addBool("UPnPNat", MyConnection.upnpNAT); chdrs.addBool("ICF", MyConnection.icf); chdrs.addString("IPv6-global", GetGlobalIp(), 2); chdrs.addString("Hashed-Nonce", dc->mNonceToHash(), 2); } else if (!strcmp(szOldContentType, "application/x-msnmsgr-transrespbody")) { const char *szListening = tFileInfo2["Listening"], *szNonce = tFileInfo2["Nonce"], *szHashedNonce = tFileInfo2["Hashed-Nonce"], *szExternalAddress = tFileInfo2["IPv4External-Addrs"], *szExternalPort = tFileInfo2["IPv4External-Port" ], *szInternalAddress = tFileInfo2["IPv4Internal-Addrs"], *szInternalPort = tFileInfo2["IPv4Internal-Port" ], *szV6Address = tFileInfo2["IPv6-Addrs"], *szV6Port = tFileInfo2["IPv6-Port" ]; if ((szNonce == NULL && szHashedNonce == NULL) || szListening == NULL) { debugLogA("Invalid data packet, exiting..."); goto LBL_Close; } directconnection* dc = p2p_getDCByCallID(szCallID, wlid); if (dc == NULL) return; if (!dc->bAccepted) dc->bAccepted = true; else return; dc->useHashedNonce = szHashedNonce != NULL; replaceStr(dc->xNonce, szHashedNonce ? szHashedNonce : szNonce); // another side reported that it will be a server. if (!strcmp(szListening, "true") && (szNonce == NULL || strcmp(szNonce, sttVoidUid))) { p2p_startConnect(ft->p2p_dest, szCallID, szV6Address, szV6Port, true); p2p_startConnect(ft->p2p_dest, szCallID, szInternalAddress, szInternalPort, false); p2p_startConnect(ft->p2p_dest, szCallID, szExternalAddress, szExternalPort, false); return; } // no, send a file via server if (!p2p_createListener(ft, dc, chdrs)) { p2p_unregisterDC(dc); if (ft != &ftl) MSN_StartP2PTransferByContact(ft->p2p_dest); else p2p_startSessions(ft->p2p_dest); return; } tResult.addString("Content-Type", "application/x-msnmsgr-transrespbody"); } else if (!strcmp(szOldContentType, "application/x-msnmsgr-transreqbody")) { const char *szHashedNonce = tFileInfo2["Hashed-Nonce"]; const char *szNonce = tFileInfo2["Nonce"]; directconnection* dc = p2p_getDCByCallID(szCallID, wlid); if (dc == NULL) { dc = new directconnection(szCallID, wlid); p2p_registerDC(dc); } dc->useHashedNonce = szHashedNonce != NULL; replaceStr(dc->xNonce, szHashedNonce ? szHashedNonce : szNonce); // no, send a file via server if (!p2p_createListener(ft, dc, chdrs)) { p2p_unregisterDC(dc); MSN_StartP2PTransferByContact(ft->p2p_dest); return; } tResult.addString("Content-Type", "application/x-msnmsgr-transrespbody"); } else return; if (!ft->p2p_isV2) p2p_getMsgId(ft->p2p_dest, -1); p2p_sendSlp(-2, ft, tResult, chdrs); } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_processSIP - processes all MSN SIP commands void CMsnProto::p2p_processSIP(ThreadData* info, char* msgbody, P2PB_Header* hdrdata, const char* wlid) { int iMsgType = 0; if (!memcmp(msgbody, "INVITE MSNMSGR:", 15)) iMsgType = 1; else if (!memcmp(msgbody, "MSNSLP/1.0 200 ", 15)) iMsgType = 2; else if (!memcmp(msgbody, "BYE MSNMSGR:", 12)) iMsgType = 3; else if (!memcmp(msgbody, "MSNSLP/1.0 603 ", 15)) iMsgType = 4; else if (!memcmp(msgbody, "MSNSLP/1.0 481 ", 15)) iMsgType = 4; else if (!memcmp(msgbody, "MSNSLP/1.0 500 ", 15)) iMsgType = 4; else if (!memcmp(msgbody, "ACK MSNMSGR:", 12)) iMsgType = 5; char* peol = strstr(msgbody, "\r\n"); if (peol != NULL) msgbody = peol+2; MimeHeaders tFileInfo, tFileInfo2; msgbody = tFileInfo.readFromBuffer(msgbody); msgbody = tFileInfo2.readFromBuffer(msgbody); const char* szContentType = tFileInfo["Content-Type"]; if (szContentType == NULL) { debugLogA("Invalid or missing Content-Type field, exiting"); return; } if (hdrdata && !hdrdata->isV2Hdr()) { if (iMsgType == 2 || (iMsgType == 1 && !strcmp(szContentType, "application/x-msnmsgr-transreqbody"))) p2p_getMsgId(wlid, 1); } switch(iMsgType) { case 1: if (!strcmp(szContentType, "application/x-msnmsgr-sessionreqbody")) p2p_InitFileTransfer(info, tFileInfo, tFileInfo2, wlid); else if (!strcmp(szContentType, "application/x-msnmsgr-transreqbody")) p2p_InitDirectTransfer(tFileInfo, tFileInfo2, wlid); else if (!strcmp(szContentType, "application/x-msnmsgr-transrespbody")) p2p_InitDirectTransfer2(tFileInfo, tFileInfo2, wlid); break; case 2: p2p_AcceptTransfer(tFileInfo, tFileInfo2, wlid); break; case 3: if (!strcmp(szContentType, "application/x-msnmsgr-sessionclosebody")) { filetransfer* ft = p2p_getSessionByCallID(tFileInfo["Call-ID"], wlid); if (ft != NULL) { if (ft->std.currentFileProgress < ft->std.currentFileSize) { ft->bCanceled = true; p2p_sendAbortSession(ft); } else { if (!(ft->std.flags & PFTS_SENDING)) ft->bCompleted = true; } p2p_sessionComplete(ft); } } break; case 4: { const char* szCallID = tFileInfo["Call-ID"]; // application/x-msnmsgr-session-failure-respbody directconnection *dc = p2p_getDCByCallID(szCallID, wlid); if (dc != NULL) { p2p_unregisterDC(dc); break; } filetransfer* ft = p2p_getSessionByCallID(szCallID, wlid); if (ft == NULL) break; ft->close(); if (!(ft->std.flags & PFTS_SENDING)) _tremove(ft->std.tszCurrentFile); p2p_unregisterSession(ft); } break; case 5: if (!strcmp(szContentType, "application/x-msnmsgr-turnsetup")) { // tFileInfo2["ServerAddress"]; // tFileInfo2["SessionUsername"]; // tFileInfo2["SessionPassword"]; } else if (!strcmp(szContentType, "application/x-msnmsgr-transudpswitch")) { // tFileInfo2["IPv6AddrsAndPorts"]; // tFileInfo2["IPv4ExternalAddrsAndPorts"]; // tFileInfo2["IPv4InternalAddrsAndPorts"]; } break; } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_processMsg - processes all MSN P2P incoming messages void CMsnProto::p2p_processMsgV2(ThreadData* info, char* msgbody, const char* wlid) { P2PV2_Header hdrdata; char *msg = hdrdata.parseMsg(msgbody); hdrdata.logHeader(this); if (hdrdata.mSessionID == 0) { if (hdrdata.mPacketLen == 0) { if (hdrdata.mOpCode & 0x02) p2p_sendAck(wlid, &hdrdata); return; } if (hdrdata.mRemSize || hdrdata.mTFCode == 0) { char msgid[128]; mir_snprintf(msgid, sizeof(msgid), "%s_%08x", wlid, hdrdata.mPacketNum); int idx; if (hdrdata.mTFCode == 0x01) { const size_t portion = hdrdata.mPacketLen + (msg - msgbody); const size_t len = portion + hdrdata.mRemSize; idx = addCachedMsg(msgid, msgbody, 0, portion, len, false); } else { size_t len = hdrdata.mPacketLen + hdrdata.mRemSize; size_t offset = getCachedMsgSize(msgid); if (offset >= len) offset -= len; idx = addCachedMsg(msgid, msg, offset, hdrdata.mPacketLen, len, false); } if (hdrdata.mRemSize == 0) { size_t newsize; if (getCachedMsg(idx, msgbody, newsize)) { unsigned id = hdrdata.mID; msg = hdrdata.parseMsg(msgbody); hdrdata.mID = id; if (hdrdata.mOpCode & 0x02) p2p_sendAck(wlid, &hdrdata); if (hdrdata.mTFCode) p2p_processSIP(info, msg, &hdrdata, wlid); mir_free(msgbody); } else clearCachedMsg(idx); } } else { if (hdrdata.mOpCode & 0x02) p2p_sendAck(wlid, &hdrdata); p2p_processSIP(info, msg, &hdrdata, wlid); } return; } if (hdrdata.mOpCode & 0x02) p2p_sendAck(wlid, &hdrdata); filetransfer* ft = p2p_getSessionByID(hdrdata.mSessionID); if (ft == NULL) return; ft->ts = time(NULL); if (hdrdata.mTFCode >= 4 && hdrdata.mTFCode <= 7) { _write(ft->fileId, msg, hdrdata.mPacketLen); ft->std.totalProgress += hdrdata.mPacketLen; ft->std.currentFileProgress += hdrdata.mPacketLen; if (ft->p2p_appID == MSN_APPID_FILE && clock() >= ft->nNotify) { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); ft->nNotify = clock() + 500; //---- send an ack: body was transferred correctly debugLogA("Transferred %I64u bytes remaining %I64u", ft->std.currentFileProgress, hdrdata.mRemSize); } if (hdrdata.mRemSize == 0) { if (ft->p2p_appID == MSN_APPID_FILE) { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); ft->complete(); } else { p2p_savePicture2disk(ft); if (!ft->p2p_isV2) p2p_sendBye(ft); } } } } void CMsnProto::p2p_processMsg(ThreadData* info, char* msgbody, const char* wlid) { P2P_Header hdrdata; msgbody = hdrdata.parseMsg(msgbody); hdrdata.logHeader(this); //---- if we got a message if (LOWORD(hdrdata.mFlags) == 0 && hdrdata.mSessionID == 0) { // MsnContact *cont = Lists_Get(wlid); // if (cont && cont->places.getCount()) // return; if (hdrdata.mPacketLen < hdrdata.mTotalSize) { char msgid[128]; mir_snprintf(msgid, sizeof(msgid), "%s_%08x", wlid, hdrdata.mID); int idx = addCachedMsg(msgid, msgbody, (size_t)hdrdata.mOffset, hdrdata.mPacketLen, (size_t)hdrdata.mTotalSize, false); char* newbody; size_t newsize; if (getCachedMsg(idx, newbody, newsize)) { p2p_sendAck(wlid, &hdrdata); p2p_processSIP(info, newbody, &hdrdata, wlid); mir_free(newbody); } else { if (hdrdata.mOffset + hdrdata.mPacketLen >= hdrdata.mTotalSize) clearCachedMsg(idx); } } else { p2p_sendAck(wlid, &hdrdata); p2p_processSIP(info, msgbody, &hdrdata, wlid); } return; } filetransfer* ft = p2p_getSessionByID(hdrdata.mSessionID); if (ft == NULL) ft = p2p_getSessionByUniqueID(hdrdata.mAckUniqueID); if (ft == NULL) return; ft->ts = time(NULL); //---- receiving redirect ----------- if (hdrdata.mFlags == 0x01) { if (WaitForSingleObject(ft->hLockHandle, INFINITE) == WAIT_OBJECT_0) { __int64 dp = (__int64)(ft->std.currentFileProgress - hdrdata.mAckDataSize); ft->std.totalProgress -= dp ; ft->std.currentFileProgress -= dp; _lseeki64(ft->fileId, ft->std.currentFileProgress, SEEK_SET); ft->tType = info->mType; ReleaseMutex(ft->hLockHandle); } } //---- receiving ack ----------- if (hdrdata.mFlags == 0x02) { ft->p2p_waitack = false; if (hdrdata.mAckSessionID == ft->p2p_sendmsgid) { if (ft->p2p_appID == MSN_APPID_FILE) { ft->bCompleted = true; p2p_sendBye(ft); } return; } if (hdrdata.mAckSessionID == ft->p2p_byemsgid) { p2p_sessionComplete(ft); return; } switch(ft->p2p_ackID) { case 1000: //---- send Data Preparation Message p2p_sendAvatarInit(ft); break; case 1001: //---- send Data Messages MSN_StartP2PTransferByContact(ft->p2p_dest); break; } ft->p2p_ackID++; return; } if (LOWORD(hdrdata.mFlags) == 0) { //---- accept the data preparation message ------ // const unsigned* pLongs = (unsigned*)msgbody; if (hdrdata.mPacketLen == 4 && hdrdata.mTotalSize == 4) { p2p_sendAck(ft->p2p_dest, &hdrdata); return; } else hdrdata.mFlags = 0x20; } //---- receiving data ----------- if (LOWORD(hdrdata.mFlags) == 0x20 || LOWORD(hdrdata.mFlags) == 0x30) { if (hdrdata.mOffset + hdrdata.mPacketLen > hdrdata.mTotalSize) hdrdata.mPacketLen = DWORD(hdrdata.mTotalSize - hdrdata.mOffset); if (ft->tTypeReq == 0 || ft->tTypeReq == info->mType) { ft->tType = info->mType; ft->p2p_sendmsgid = hdrdata.mID; } __int64 dsz = ft->std.currentFileSize - hdrdata.mOffset; if (dsz > hdrdata.mPacketLen) dsz = hdrdata.mPacketLen; if (ft->tType == info->mType) { if (dsz > 0 && ft->fileId >= 0) { if (ft->lstFilePtr != hdrdata.mOffset) _lseeki64(ft->fileId, hdrdata.mOffset, SEEK_SET); _write(ft->fileId, msgbody, (unsigned int)dsz); ft->lstFilePtr = hdrdata.mOffset + dsz; __int64 dp = ft->lstFilePtr - ft->std.currentFileProgress; if (dp > 0) { ft->std.totalProgress += dp; ft->std.currentFileProgress += dp; if (ft->p2p_appID == MSN_APPID_FILE && clock() >= ft->nNotify) { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); ft->nNotify = clock() + 500; } } //---- send an ack: body was transferred correctly debugLogA("Transferred %I64u bytes out of %I64u", ft->std.currentFileProgress, hdrdata.mTotalSize); } if (ft->std.currentFileProgress >= hdrdata.mTotalSize) { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std); p2p_sendAck(ft->p2p_dest, &hdrdata); if (ft->p2p_appID == MSN_APPID_FILE) { ft->ts = time(NULL); ft->p2p_waitack = true; ft->complete(); } else { p2p_savePicture2disk(ft); p2p_sendBye(ft); } } } } if (hdrdata.mFlags == 0x40 || hdrdata.mFlags == 0x80) { p2p_sendAbortSession(ft); p2p_unregisterSession(ft); } } ///////////////////////////////////////////////////////////////////////////////////////// // p2p_invite - invite another side to transfer an avatar void CMsnProto::p2p_invite(unsigned iAppID, filetransfer* ft, const char *wlid) { const char* szAppID; switch(iAppID) { case MSN_APPID_FILE: szAppID = "{5D3E02AB-6190-11D3-BBBB-00C04F795683}"; break; case MSN_APPID_AVATAR: szAppID = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"; break; case MSN_APPID_CUSTOMSMILEY: szAppID = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"; break; case MSN_APPID_CUSTOMANIMATEDSMILEY: szAppID = "{A4268EEC-FEC5-49E5-95C3-F126696BDBF6}"; break; default: return; } ft->p2p_type = iAppID; ft->p2p_acksessid = MSN_GenRandom(); mir_free(ft->p2p_callID); ft->p2p_callID = getNewUuid(); MsnContact* cont = Lists_Get(ft->std.hContact); if (cont == NULL) return; if (ft->p2p_dest == NULL) { ft->p2p_isV2 = (cont->cap2 & capex_SupportsPeerToPeerV2) != 0 || (cont->cap1 >> 28) >= 10; ft->p2p_dest = mir_strdup(wlid ? wlid : cont->email); } char* pContext = NULL; size_t cbContext = 0; switch (iAppID) { case MSN_APPID_FILE: { cbContext = sizeof(HFileContext); pContext = (char*)malloc(cbContext); HFileContext* ctx = (HFileContext*)pContext; memset(pContext, 0, cbContext); if (ft->p2p_isV2) { cbContext -= 64; ctx->ver = 2; } else { ctx->ver = 3; ctx->id = 0xffffffff; } ctx->len = (unsigned)cbContext; ctx->type = MSN_TYPEID_FTNOPREVIEW; ctx->dwSize = ft->std.currentFileSize; TCHAR* pszFiles = _tcsrchr(ft->std.tszCurrentFile, '\\'); if (pszFiles) pszFiles++; else pszFiles = ft->std.tszCurrentFile; wchar_t *fname = mir_t2u(pszFiles); wcsncpy(ctx->wszFileName, fname, MAX_PATH); mir_free(fname); ft->p2p_appID = MSN_APPID_FILE; } break; default: ft->p2p_appID = MSN_APPID_AVATAR2; if (ft->p2p_object == NULL) { delete ft; return; } ezxml_t xmlo = ezxml_parse_str(NEWSTR_ALLOCA(ft->p2p_object), strlen(ft->p2p_object)); ezxml_t xmlr = ezxml_new("msnobj"); const char* p; p = ezxml_attr(xmlo, "Creator"); if (p != NULL) ezxml_set_attr(xmlr, "Creator", p); p = ezxml_attr(xmlo, "Size"); if (p != NULL) { ezxml_set_attr(xmlr, "Size", p); ft->std.totalBytes = ft->std.currentFileSize = _atoi64(p); } p = ezxml_attr(xmlo, "Type"); if (p != NULL) ezxml_set_attr(xmlr, "Type", p); p = ezxml_attr(xmlo, "Location"); if (p != NULL) ezxml_set_attr(xmlr, "Location", p); p = ezxml_attr(xmlo, "Friendly"); if (p != NULL) ezxml_set_attr(xmlr, "Friendly", p); p = ezxml_attr(xmlo, "SHA1D"); if (p != NULL) ezxml_set_attr(xmlr, "SHA1D", p); p = ezxml_attr(xmlo, "SHA1C"); if (p != NULL) ezxml_set_attr(xmlr, "SHA1C", p); pContext = ezxml_toxml(xmlr, false); cbContext = strlen(pContext)+1; ezxml_free(xmlr); ezxml_free(xmlo); break; } bool sessionExist = p2p_sessionRegistered(ft); if (!sessionExist) { p2p_registerSession(ft); unsigned short status = getWord(ft->std.hContact, "Status", ID_STATUS_OFFLINE); if ((myFlags & 0x4000000) && cont->places.getCount() <= 1 && status != ID_STATUS_OFFLINE && status != ID_STATUS_INVISIBLE && m_iStatus != ID_STATUS_INVISIBLE) { if (ft->p2p_isV2) { if (cont->places.getCount() && cont->places[0].cap1 & cap_SupportsP2PBootstrap) { char wlid[128]; mir_snprintf(wlid, SIZEOF(wlid), strcmp(cont->places[0].id, sttVoidUid) ? "%s;%s" : "%s", cont->email, cont->places[0].id); if (!MSN_GetThreadByContact(wlid, SERVER_P2P_DIRECT)) p2p_inviteDc(ft, wlid); else p2p_invite(ft->p2p_type, ft, wlid); free(pContext); return; } } else { const char *wlid = cont->email; if (cont->cap1 & cap_SupportsP2PBootstrap) { if (!MSN_GetThreadByContact(wlid, SERVER_P2P_DIRECT)) p2p_inviteDc(ft, wlid); else p2p_invite(ft->p2p_type, ft, wlid); free(pContext); return; } } } } if (!ft->bAccepted) ft->p2p_sessionid = MSN_GenRandom(); ptrA szContextEnc( mir_base64_encode((PBYTE)pContext, (unsigned)cbContext)); int cbContextEnc = lstrlenA(szContextEnc); MimeHeaders chdrs(10); chdrs.addString("EUF-GUID", szAppID); chdrs.addULong("SessionID", ft->p2p_sessionid); chdrs.addULong("AppID", ft->p2p_appID); chdrs.addString("Context", szContextEnc); MimeHeaders tResult(8); tResult.addString("CSeq", "0 "); tResult.addString("Call-ID", ft->p2p_callID); tResult.addLong("Max-Forwards", 0); tResult.addString("Content-Type", "application/x-msnmsgr-sessionreqbody"); if (iAppID != MSN_APPID_FILE) ft->p2p_waitack = true; if (ft->p2p_isV2 && ft->std.currentFileNumber == 0) { for (int i = 0; i < cont->places.getCount(); ++i) { char wlid[128]; mir_snprintf(wlid, SIZEOF(wlid), strcmp(cont->places[i].id, sttVoidUid) ? "%s;%s" : "%s", cont->email, cont->places[i].id); p2p_sendSlp(-2, ft, tResult, chdrs, wlid); } } else p2p_sendSlp(-2, ft, tResult, chdrs, wlid); free(pContext); } void CMsnProto::p2p_inviteDc(filetransfer* ft, const char *wlid) { directconnection* dc = new directconnection(szUbnCall, wlid); p2p_registerDC(dc); MimeHeaders tResult(8); tResult.addString("CSeq", "0 "); tResult.addString("Call-ID", dc->callId); tResult.addLong("Max-Forwards", 0); tResult.addString("Content-Type", "application/x-msnmsgr-transreqbody"); MimeHeaders chdrs(12); chdrs.addString("Bridges", "TCPv1 SBBridge"); chdrs.addLong("NetID", MyConnection.extIP); chdrs.addString("Conn-Type", MyConnection.GetMyUdpConStr()); chdrs.addBool("UPnPNat", MyConnection.upnpNAT); chdrs.addBool("ICF", MyConnection.icf); chdrs.addString("IPv6-global", GetGlobalIp(), 2); chdrs.addString("Hashed-Nonce", dc->mNonceToHash(), 2); chdrs.addString("SessionID", "0"); chdrs.addString("SChannelState", "0"); chdrs.addString("Capabilities-Flags", "1"); p2p_sendSlp(-2, ft, tResult, chdrs, wlid); } /* void CMsnProto::p2p_sendSessionAck(filetransfer* ft) { MimeHeaders tResult(8); tResult.addString("CSeq", "0 "); tResult.addString("Call-ID", sttVoidUid); tResult.addLong("Max-Forwards", 0); tResult.addString("Content-Type", "application/x-msnmsgr-transdestaddrupdate"); MimeHeaders chdrs(8); chdrs.addString("IPv4ExternalAddrsAndPorts", mir_strdup(MyConnection.GetMyExtIPStr()), 6); chdrs.addString("IPv4InternalAddrsAndPorts", mir_strdup(MyConnection.GetMyExtIPStr()), 6); chdrs.addString("SessionID", "0"); chdrs.addString("SChannelState", "0"); chdrs.addString("Capabilities-Flags", "1"); p2p_sendSlp(-3, ft, tResult, chdrs); } */ void CMsnProto::p2p_sessionComplete(filetransfer* ft) { if (ft->p2p_appID != MSN_APPID_FILE) p2p_unregisterSession(ft); else if (ft->std.flags & PFTS_SENDING) { if (ft->openNext() == -1) { bool success = ft->std.currentFileNumber >= ft->std.totalFiles && ft->bCompleted; ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, success ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, ft, 0); } else { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0); p2p_invite(ft->p2p_appID, ft, NULL); } } else { ProtoBroadcastAck(ft->std.hContact, ACKTYPE_FILE, ft->bCompleted ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, ft, 0); p2p_unregisterSession(ft); } } char* P2PV2_Header::parseMsg(char *buf) { unsigned char hdrLen1 = *(unsigned char*)buf; mOpCode = *(unsigned char*)(buf + 1); mPacketLen = _htons(*(unsigned short*)(buf + 2)); mID = _htonl(*(unsigned*)(buf + 4)); char* buf1 = buf + hdrLen1; for (char *tlvp = buf + 8; tlvp < buf1 && *tlvp; tlvp += 2 + tlvp[1]) { switch (*tlvp) { case 1: mCap = tlvp; break; case 2: mAckUniqueID = _htonl(*(unsigned*)(tlvp + 4)); break; case 3: break; } } if (mPacketLen == 0) return buf + hdrLen1; unsigned char hdrLen2 = *(unsigned char*)buf1; mTFCode = *(unsigned char*)(buf1 + 1); mPacketNum = _htons(*(unsigned short*)(buf1 + 2)); mSessionID = _htonl(*(unsigned*)(buf1 + 4)); char* buf2 = buf1 + hdrLen2; for (char *tlvp1 = buf1 + 8; tlvp1 < buf2 && *tlvp1; tlvp1 += 2 + tlvp1[1]) { switch (*tlvp1) { case 1: mRemSize = _htonl64(*(unsigned __int64*)(tlvp1 + 2)); break; } } mPacketLen -= hdrLen2; return buf1 + hdrLen2; } char* P2PV2_Header::createMsg(char *buf, const char* wlid, CMsnProto *ppro) { unsigned char hdrLen1 = 8 + (mAckUniqueID ? 6 : 0) + (mCap ? 2 + mCap[1] : 0); unsigned char comp = hdrLen1 & 3; hdrLen1 += comp ? 4 - comp : 0; unsigned char hdrLen2 = mPacketLen ? (8 + (mRemSize ? 10 : 0)) : 0; comp = hdrLen2 & 3; hdrLen2 += comp ? 4 - comp : 0; mID = ppro->p2p_getMsgId(wlid, mPacketLen + hdrLen2); memset(buf, 0, hdrLen1 + hdrLen2); *(unsigned char*)(buf + 0) = hdrLen1; *(unsigned char*)(buf + 1) = mOpCode; *(unsigned short*)(buf + 2) = _htons(mPacketLen + hdrLen2); *(unsigned*)(buf + 4) = _htonl(mID); char *buf1 = buf + 8; if (mAckUniqueID) { *(unsigned char*)buf1 = 2; *(unsigned char*)(buf1 + 1) = 4; *(unsigned*)(buf1 + 2) = _htonl(mAckUniqueID); buf1 += 6; } if (mCap) { unsigned len = 2 + mCap[1]; memcpy(buf1, mCap, len); buf1 += len; } buf1 = buf + hdrLen1; if (hdrLen2 == 0) return buf1; *(unsigned char*)(buf1 + 0) = hdrLen2; *(unsigned char*)(buf1 + 1) = mTFCode; *(unsigned short*)(buf1 + 2) = _htons(mPacketNum); *(unsigned*)(buf1 + 4) = _htonl(mSessionID); if (mRemSize) { *(unsigned char*)(buf1 + 8) = 1; *(unsigned char*)(buf1 + 9) = 8; *(unsigned __int64*)(buf1 + 10) = _htonl64(mRemSize); } return buf1 + hdrLen2; } char* P2P_Header::createMsg(char *buf, const char* wlid, CMsnProto *ppro) { if (!mID) mID = ppro->p2p_getMsgId(wlid, 1); memcpy(buf, &mSessionID, 48); return buf + 48; }