/*
Plugin of Miranda IM for communicating with users of the MSN Messenger protocol.
Copyright (c) 2012-2013 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->MSN_DebugLog("--- Printing message header");
ppro->MSN_DebugLog(" SessionID = %08X", mSessionID);
ppro->MSN_DebugLog(" MessageID = %08X", mID);
#ifndef __GNUC__
ppro->MSN_DebugLog(" Offset of data = %I64u", mOffset);
ppro->MSN_DebugLog(" Total amount of data = %I64u", mTotalSize);
#else
ppro->MSN_DebugLog(" Offset of data = %llu", mOffset);
ppro->MSN_DebugLog(" Total amount of data = %llu", hdrdata->mTotalSize);
#endif
ppro->MSN_DebugLog(" Data in packet = %lu bytes", mPacketLen);
ppro->MSN_DebugLog(" Flags = %08X", mFlags);
ppro->MSN_DebugLog(" Acknowledged session ID: %08X", mAckSessionID);
ppro->MSN_DebugLog(" Acknowledged message ID: %08X", mAckUniqueID);
#ifndef __GNUC__
ppro->MSN_DebugLog(" Acknowledged data size: %I64u", mAckDataSize);
#else
ppro->MSN_DebugLog(" Acknowledged data size: %llu", mAckDataSize);
#endif
ppro->MSN_DebugLog("------------------------");
}
void P2PV2_Header::logHeader(CMsnProto *ppro)
{
ppro->MSN_DebugLog("--- Printing message header");
ppro->MSN_DebugLog(" SessionID = %08X", mSessionID);
ppro->MSN_DebugLog(" MessageID = %08X", mID);
#ifndef __GNUC__
ppro->MSN_DebugLog(" Remaining amount of data = %I64u", mRemSize);
#else
ppro->MSN_DebugLog(" Remaining amount of data = %llu", mTotalSize);
#endif
ppro->MSN_DebugLog(" Data in packet = %lu bytes", mPacketLen);
ppro->MSN_DebugLog(" Packet Number = %lu", mPacketNum);
ppro->MSN_DebugLog(" Operation Code = %08X", mOpCode);
ppro->MSN_DebugLog(" TF Code = %08X", mTFCode);
ppro->MSN_DebugLog(" Acknowledged message ID: %08X", mAckUniqueID);
ppro->MSN_DebugLog("------------------------");
}
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) hNetlibUser, (LPARAM)&nlb);
if (sb == NULL)
{
MSN_DebugLog("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;
deleteSetting(ft->std.hContact, "AvatarHash");
SendBroadcast(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 = MSN_GetImageFormat(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);
SendBroadcast(AI.hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &AI, 0);
char *filename = mir_utf8encodeT(AI.filename);
MSN_DebugLog("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)
{
MSN_DebugLog(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)
{
MSN_DebugLog(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)
{
MSN_DebugLog(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)
{
MSN_DebugLog(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)
{
MSN_DebugLog(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)
{
MSN_DebugLog(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);
}
MSN_DebugLog("Connecting to %s:%d", tConn.szHost, tConn.wPort);
info->s = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)hNetlibUser, (LPARAM)&tConn);
if (info->s == NULL)
{
TWinErrorCode err;
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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:
MSN_DebugLog("Incoming connection timed out, closing file transfer");
MSN_StartP2PTransferByContact(info->mInitialContactWLID);
LBL_Error:
MSN_DebugLog("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)
{
MSN_DebugLog("Invalid input data, exiting");
return false;
}
if ((p = buf.surelyRead(4)) == NULL)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
SendBroadcast(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std);
ft->nNotify = clock() + 500;
}
}
else
MSN_DebugLog(" 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;
MSN_DebugLog("File send thread started");
switch(WaitForSingleObject(info->hWaitEvent, 6000))
{
case WAIT_FAILED:
MSN_DebugLog("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)
{
MSN_DebugLog("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)
SendBroadcast(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);
}
MSN_DebugLog("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;
MSN_DebugLog("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);
}
MSN_DebugLog("p2p_fileActiveThread() completed: connecting to '%s'", info->mServer);
}
void __cdecl CMsnProto::p2p_filePassiveThread(void* arg)
{
ThreadData* info = (ThreadData*)arg;
MSN_DebugLog("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);
}
MSN_DebugLog("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) {
MSN_DebugLog("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)
{
MSN_DebugLog("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);
MSN_FreeVariant(&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");
MSN_DebugLog("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);
// MSN_DebugLog("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);
MSN_DebugLog("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 (currently not 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 our webcam data (currently not 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 (currently not supported)"),
// MSN_ALLOW_MSGBOX | MSN_SHOW_ERROR);
p2p_sendStatus(ft, 603);
p2p_unregisterSession(ft);
break;
default:
p2p_sendStatus(ft, 603);
p2p_unregisterSession(ft);
MSN_DebugLog("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)
{
MSN_DebugLog("Ignoring invalid invitation: CallID='%s', Branch='%s'", szCallID, szBranch);
return;
}
if (szConnType == NULL || szUPnPNat == NULL || szICF == NULL || szNetID == NULL)
{
MSN_DebugLog("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
SendBroadcast(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;
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
MSN_DebugLog("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)
{
SendBroadcast(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std);
ft->nNotify = clock() + 500;
//---- send an ack: body was transferred correctly
MSN_DebugLog("Transferred %I64u bytes remaining %I64u", ft->std.currentFileProgress, hdrdata.mRemSize);
}
if (hdrdata.mRemSize == 0)
{
if (ft->p2p_appID == MSN_APPID_FILE)
{
SendBroadcast(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)
{
SendBroadcast(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_DATA, ft, (LPARAM)&ft->std);
ft->nNotify = clock() + 500;
}
}
//---- send an ack: body was transferred correctly
MSN_DebugLog("Transferred %I64u bytes out of %I64u", ft->std.currentFileProgress, hdrdata.mTotalSize);
}
if (ft->std.currentFileProgress >= hdrdata.mTotalSize)
{
SendBroadcast(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;
SendBroadcast(ft->std.hContact, ACKTYPE_FILE, success ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, ft, 0);
}
else
{
SendBroadcast(ft->std.hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ft, 0);
p2p_invite(ft->p2p_appID, ft, NULL);
}
}
else
{
SendBroadcast(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;
}