/ * Msnp2p.cpp - MSN P2P Protocol
Copyright (c) 2003-2004 by olivier goffart
*********************************************************** *********************** * * * this program is free software; you can redistribute it and / or modify * * it under the terminal 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. * * * *************** *********************************************************** ******** /
#include "msnp2p.h"
#include
// qt # include
// kde # include
// kopete # include
MSNP2P :: MSNP2P (QObject * parent, const char * name): QObject (parent, name) {m_file = 0l; m_Sfile = 0L; m_Rfile = 0L; m_msgIdentifier = 0; m_sessionId = 0; m_totalDataSize = 0; m_offset = 0; m_kopetetransfer = 0L;}
MSNP2P :: ~ msnp2p () {if (m_file) delete m_file; else delete m_rfile; delete m_sfile;}
Void msnp2p :: slotreadmessage (const qbyteaRray & msg) {qstring messageHeader = qcstring (msg.data (), (msg.find ('/ 0') == - 1)? msg.size (): msg.find ('/ 0 ')); QREGEXP RX ("Content-Type: ([A-ZA-ZA0-9 $! * / /// -] *)"); rx.search (MessageHeader); QString Type = rx.cap (1 );
IF (type == "Application / X-msnmsgrp2p") {// Get the starting position of the 48-bytes bunary header unsigned int startbinheader = 0; BOOL JUSTCR = false; while (StartbinHeader
// Read some interesting field from the binary header unsigned int dataMessageSize = (int) (unsigned char) (msg.data () [startBinHeader 24]) (int) ((unsigned char) msg.data () [startBinHeader 25]) * 256; unsigned int totalsize = (int) (msg.data () [STARTBINHEADER 16]) (INT) ((unsigned char) msg.data () [startbinheader 17]) * 256 (int) (unsigned char) msg.data () [startbinheader 18]) * 256 * 256 (int) ((unsigned char) msg.data () [startbinheader 19]) * 256 * 256 * 256; unsigned int DataOffset = (int) (msg.data () [startbinheader 8]) (int) ((unsigned char) msg.data () [startbinheader 9]) * 256 (int) * 256 (int (unsigned char) msg.data () [startbinheader 10]) * 256 * 256 (int) (unsigned char) msg.data () [startbinheader 11]) * 256 * 256 * 256; if ( DataMessagesize == 0) {kddebug (14140) << "MSNP2P :: SlotreadMessage: i do not care, it's a Ack - flag = << (int) (msg.data () [STARTBINHEADER 28] << Endl; Return;}
if (msg.size ()
QString DataMessage = QCString ((msg.data () StartbinHeader 48), DataMessagesize;
IF (m_msghandle.Isempty ()) {// if these addresses bere not Previously set, Get IT, The Yy Should Be supplied in The First Message At last. Qregexp RX ("To:
Rx = QRegexp ("from:
IF (m_rfile) // we are already Downloading Something to this file {// m_file-> file () -> WriteBlock ((Msg.Data () StartbinHeader 48), DataMessagesize; m_rfile-> WriteBlock ((MSG.) Data () StartbinHeader 48), DataMessagesize;
IF (m_kopetetransfer) m_kopetetransfer-> slotprocessed (DataOffset DataMessageSize);
if (dataOffset dataMessageSize> = totalSize) // the file is complete {if (m_file) {m_file-> close (); emit fileReceived (m_file, m_obj); m_file = 0; m_Rfile = 0L;} else {if (m_kopeteTransfer ) m_kopetetransfer-> slotcomplete (); m_rfile-> close (); delete m_rfile; m_rfile = 0L;} / * delete m_file; * /
// deleteLater (); // deletelater ();}} else};}} else};}} else {kddebug (14141) << "MSNP2P :: SlotReadMessage: DataMessage: << DataMessage <<
IF (msg.data () [STARTBINHEADER 48] == '/ 0') {// this can be only the data preparaion message. prepare to download m_file = new ktempfile (locatelocal ("tmp", "msnpicture-") , ".png"); m_file-> setAutodelete (true); m_rfile = m_file-> file ();} else if (DataMessage.Contains ("invite")) {// parse the message to get some info for replying QRegexp RX ("; branch = // {([0-9A-f // -] *) //} / r / n"); rx.search (data); m_branch = rx.cap (1);
Rx = QRegexp ("Call-id: // {([0-9A-f // -] *) //} / r / n"); rx.search (dataMessage); m_callid = rx.cap (1) ; if (! m_kopetetransfer) // it's the first invite message {rx = QRegexp ("sessionid: ([0-9] *) / r / n"); rx.search (data); m_sessionID = rx.cap (1 ) .TOUINT (); rx = qregexp ("AppID: ([0-9] *) / r / n"); rx.search (data); unsigned long int AppID = rx.cap (1) .touint () ; If (appid == 1) // The Peer Ask for a MSN Picture, OR EMOTICON Download. {// Currently, We Always Send The Display Picture
// sent the ok message. Qstring content = "sessionid:" qstring :: Number (m_sessionid) "/ r / n / r / n"; MakeMsNSLPMESSAGE (OK, Content); // prepare to send the file m_sfile = NEW QFILE ("AppData", "MsnPicture -" M_MyHandle.Lower (). Replace (QRegexp ("[./ ~]"), "-") ". PNG"); if (! m_sfile > open (io_readonly) {/ * TODO: ERROR? * /} // send the data preparation message qbyteaRray INITM (4); initm.fill ('/ 0'); sendp2pMessage (Initm); m_totalDataSize = m_sfile-> size (); Qtimer :: SINGLESHOT (10, this, slot (slotsenddata ())); // go for upload} else if (appid == 2) // The Peer Want To Transfer a file. {// extract the context From the invitation contents rx = qregexp ("Context: ([0-9A-ZA-Z / =] *)") "); rx.search (DataMessage); QString context = rx.cap (1);
// context is a base64 Encode Dump of the internal memory of msn messanger. // The filesis is contained in the bytes 8..11 // The filename is from the byte 19 // i don't know what Other Fields Are. QByteArray BinaryContext; Kcodecs :: Base64Decode (CONTEXT.UTF8 (), BinaryContext); IF (BinaryContext.Size () <21) // Security, (Don't crash) return; // Todo: Handle Error // the filename IS Conteding in the context from the 19st char to the end. (in UTF-16) QTextCodec * Codec = QTextcodec :: CodecForname ("ISO-10646-UCS-2"); if (! CODEC) Return; // Abort () Qstring filename = codec-> Tounicode (binarycontext.data () 19, binarycontext.size () - 19-16); filename = filename.left (filename.find (QCHAR ('/ 0'))); //// THE SIZE IS Placed In The Context in the bytes 8..12 (Source: The Amsn Code) unsigned long int filesize = (binaryContext [8]) (BinaryContext [9]) * 256 (unsigned char) (BinaryContext [ 10]) * 65536 (unsigned char) (binaryContext [11]) * 16777216; // ugly hack to get the KopeteContact KopeteContact * c = 0L;. If (parent ()) {KopeteMessageManager * kmm = dynamic_cast
. Connect (KopeteTransferManager :: transferManager (), SIGNAL (refused (const KopeteFileTransferInfo &)), this, SLOT (slotFileTransferRefused (const KopeteFileTransferInfo &))); // show a dialog to ask the transfer KopeteTransferManager :: transferManager () -> askIncomingTransfer (c, filename, filesize, QString :: null, QString :: number (m_sessionId) ":" m_branch ":" m_CallID);} else // unknwon AppID {makeMSNSLPMessage (ERROR, QString :: null); }} // end of if (m_kopeteTranfer) else // we are nogitiating a complex invitaiton (a file transfer) {// it's the second INVITE message // dirrect connection is not yet implemented, use the connection via MSNP2P QString content = " Bridge: tcpv1 / r / n "" Listening: false / r / n "" Nonce: {00000000-0000-0000-0000-0000000000} / r / n / r / n ";
(! M_Rfile-> open (IO_WriteOnly)) makeMSNSLPMessage (OK, content);; m_Rfile = new QFile (. M_kopeteTransfer-> destinationURL () path ()) if {if (m_kopeteTransfer) {// TODO: handle the QFILE error m_kopeteTransfer -> sloterror (kio :: err_cannot_open_for_writing, i18n ("cannot open file for write"); m_kopeteTransfer = 0L; return;} AbortcurrentTransfer ();}}} else if (DataMessage.Contains ("BYE")) {// Deletelater ();}}} else {kddebug (14140) << "MSNSwitchboardsocket :: slotreadMessage: unknown type '<< type << endl;}
}
void MSNP2P :: requestDisplayPicture (const QString & myHandle, const QString & msgHandle, QString msnObject) {// reset some field / * m_file = 0l; m_Sfile = 0L; m_msgIdentifier = 0; m_sessionId = 0; m_totalDataSize = 0; m_offset = 0; * / M_sessionID = 0; m_myhandle = myhandle; m_msghandle = msghandle; m_obj = msnobject;
MsnObject = QString :: fromutf8 (kcodecs :: base64encode (msnobject.utf8 ())); msnObject.replace ("=", qstring :: null);
Unsigned long int sessid = rand ()% 0xfffffffff00 4; m_branch = qstring :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16) qstring :: Number ((unsigned long int) rand ()% 0xAAFF 0x1111, 16) "-" qstring :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16) "-" qstring :: Number ((unsigned long int) rand ()% 0xAAFF 0x1111, 16) "-" qstring :: Number (rand ()% 0xaaff 0x1111, 16) "-" qstring :: Number ((unsigned long int) rand ()% 0xAAFF 0x1111, 16 ) QString :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16) qstring :: Number ((unsigned long int) rand ()% 0xAAFF 0x1111, 16); m_callid = qstring :: Number (Unsigned long int) rand ()% 0xaaff 0x1111, 16) qstring :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16) "-" QString :: Number ((unsigned long) INT) Rand ()% 0xaaff 0x1111, 16) "-" qstring :: Number ()% 0xaaff 0x1111, 16) "-" qstring :: Number (rand () % 0XAAFF 0x1111, 16) "-" QString :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16) qstring :: Number ((unsigned long int) rand ()% 0xaaf F 0x1111, 16) qstring :: Number ((unsigned long int) rand ()% 0xaaff 0x1111, 16);; qstring content = "EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6} / r / N "" sessionid: " qstring :: Number (sessid) " / r / n "" APPID: 1 / r / n "" Context: " MSNObject " / R / N / R / N "; MakemsNSLPMESSAGE INVITE, Content;
void MSNP2P :: makeMSNSLPMessage (MessageType type, QString content) {QString contentType = QString ( "application / x-msnmsgr-sessionreqbody"); QString method; QString CSeq; switch (type) {case INVITE: method = "INVITE MSNMSGR:" M_MSGHANDLE "MSNSLP / 1.0"; CSEQ = "0"; Break; Case Decline: method = "msnslp / 1.0 603 decline"; cseq = "1"; Break; Case error: contentType = "null"; method = " MSNSLP / 1.0 500 Internal Error "; CSEQ =" 1 "; Break; Case OK: IF (M_kopeteTransfer) ContentTyPE =" Application / X-msnmsgr-TransReqbody "; method =" MSNSLP / 1.0 200 OK "; cseq =" 1 " ; break; case BYE: contentType = "application / x-msnmsgr-sessionclosebody"; method = "BYE MSNMSGR:" m_msgHandle "MSNSLP / 1.0"; CSeq = "0"; break;} QCString dataMessage = QString (method " / r / n "" to:
kdDebug (14141) << k_funcinfo << dataMessage << endl; sendP2PMessage (dataMessage);} void MSNP2P :: sendP2PMessage (const QByteArray & dataMessage) {bool transferStarted = (m_Rfile || m_Sfile); // we are stransfering binary is any of The File EXISTS QcString Message HEADER = QString ("MIME-VERSION: 1.0 / R / N" "Content-Type: Application / X-msnmsGRP2P / R / N" "P2P-DEST:" M_MSGHANDLE "/ R / N / R / N "). UTF8 ();
QbyteaRray Binheader (48); binheader.fill ('/ 0'); // Fill with 0 for starting
IF (m_msgidentifier == 0) m_msgidentifier = rand ()% 0x0ffffff0 4; Else IF (m_offset == 0) m_msgidentifier ;
// sessionid unsigned long int sessionid = transportstarted? M_sessionid: 0; binheader [0] = (char) (sessionier 256); binheader [1] = (char) (sessionID / 256)% 256) Binheader [2] = (CHAR) (sessionID / (256 * 256))% 256); binheader [3] = (char) (sessionid / (256 * 256 * 256))% 256);
// MessageID Binheader [4] = (char) (m_msgidentifier% 256); binheader [5] = (char) (m_msgidentifier / 256)% 256); binheader [6] = (char) (( Unsigned long int) (M_Msgidentifier / (256 * 256))% 256); binheader [7] = (char) (M_Msgidentifier / (256 * 256 * 256))% 256);
// offset binheader [8] = (char) (m_offset% 256); binheader [9] = (char) (m_offset / 256)% 256); binheader [10] = (char) (( Unsigned long int) (m_offset / (256 * 256))% 256); binheader [11] = (char) (M_Offset / (256 * 256 * 256))% 256);
Unsigned int size = dataMessage.size ();
IF (m_totaldatasize) // it's a splitted message {binheader [16] = (char) (m_totaldatasize% 256); binheader [17] = (char) (M_TotalDataSize / 256)% 256); binheader [ 18] = (CHAR) (M_TotalDataSize / (256 * 256))% 256); binheader [19] = (char) (M_TotalDataSize / (256 * 256 * 256))) % 256); // update offset m_offset = size; if (m_offset> = m_totalDataSize) {// message completely sent, reset values m_offset = 0; m_totalDataSize = 0;}} else // not a splitted message, the total size is THE CURRENT SIZE {Binheader [16] = (char) size% 256; binheader [17] = (int) Size / 256;}
// Message Size Binheader [24] = (char) Size% 256; Binheader [25] = (int) Size / 256;
// Ack sessionid binheader [32] = (char) (Rand ()% 256); binheader [33] = (char) (RAND ()% 256); binheader [34] = (char) (rand ()% 256 Binheader [35] = (char) (RAND ()% 256);
/ * binheader [32] = 0xde; binheader [33] = 0xc7; binheader [34] = 0x07; binheader [35] = 0x14; * /
// Merge All in a unique message qbytearray data () binheader.size () DataMessage.Size () 4); for (unsigned int f = 0; f IF (TransferStarted) {////// The footer Ends with / 1 (messageHeader.Length () binheader.size () DataMessage.size () 3] = '/ 1';} // dend the message Emit Sendcommand ("MSG", "D", True, Data, True;} Void msnp2p :: sendp2pack (const char * originalhead) { QcString MessageHeader = QString ("MIME-VERSION: 1.0 / R / N" "Content-Type: Application / X-msnmsGRP2P / R / N" "P2P-DEST:" M_MSGHANDLE "/ R / N / R / N" .备 .备 (); QbyteaRray Binheader (48); binheader.fill ('/ 0'); // Fill with 0 for starting // sessionid binheader [0] = OriginalHeader [0]; binheader [1] = OriginalHeader [1]; binheader [2] = OriginalHeader [2]; binheader [3] = OriginalHeader [3]; // MessageID bool a = false; if (m_msgIdentifier == 0) {m_msgIdentifier = rand ()% 0xFFFFFE00 10; a = true;} else m_msgIdentifier ; binHeader [4] = (char) (m_msgIdentifier% 256); binHeader [ 5] = ((unsigned long int) (m_msgidentifier / 256); binheader [6] = (char) (M_Msgidentifier / (256 * 256))% 256); binheader [ 7] = (CHAR) (M_Msgidentifier / (256 * 256 * 256))% 256); IF (a) m_msgidentifier- = 4; // Total Size Binhead [16]; binheader [17] = OriginalHeader [17]; binheader [18] = OriginalHeader [18]; binheader [19] = OriginalHeader [19]; binheader [20] = OriginalHeader [20]; binheader [21] = OriginalHeader [21]; binheader [22] = OriginalHeader [22]; binheader [23] = OriginalHeader [23]; // flag binheader [28] = (char) 0x02; // Ack sessionid binheader [32] = OriginalHeader [4]; binheader [33] = OriginalHeader [5]; binheader [34] = OriginalHeader [6]; binheader [35] = OriginalHeader [7]; // Ack unique ID BinHeader [36] = OriginalHeader [32]; binheader [37] = OriginalHeader [33]; binheader [38] = OriginalHeader [34]; binheader [39] = OriginalHeader [35]; // Ack Data Size Binheader [40] = OriginalHeader [16]; binheader [41] = OriginalHeader [17]; binheader [42] = OriginalHeader [18]; binheader [43] = OriginalHeader [19]; binheader [44] = OriginalHeader [20]; binheader [45] = OriginalHeader [21]; binheader [46] = OriginalHeader [22]; binheader [47] = OriginalHeader [23]; QbyteaArray Data (MessageHeader.Length () binheader.size () 4); for (unsigned int f = 0; f Emit Sendcommand ("MSG", "D", True, Data, True;} Void msnp2p :: slotsenddata () {if (! m_sfile) return Char Data [1200]; int BytesRead = m_sfile-> readblock (data, 1200); QByteArray Database; for (int F = 0; f // kddebug (14140) << "MSNP2P :: slotsenddata: offset =" << m_offset << "size =" << BytesRead << "Totalsize =" << m_totalDataSize << "SENT =" << m_offset bytesRead < Sendp2pMessage (DATABA); IF (m_totaldatasize == 0) // This Has Been Reseted Bacause The File Is Completely send {// kd (14140) << "MSNP2P :: SlotsendData: finished! Wait for the Bye Message << Endl; delete m_sfile; m_sfile = 0L; m_sessionID = 0;} else qtimer :: Singleshot (10, this, slot (slotsenddata ()));} void MSNP2P :: slotTransferAccepted (KopeteTransfer * transfer, const QString & / * filename * /) {QStringList internalId = QStringList :: split ( ":", transfer-> info () internalId ().); if (internalId [0]. Touint () == m_sessionid) {Qobject :: Connect (TransferCaled ()), this, slot (AbortcurrentTransfer ()))); QOBject :: Connect (Transfer, Signal (DESTROYED ()), this, slot SlotkopeteTransferDestroyed ())); M_branch = internalid [1]; qstring callid = internalid [2]; Qstring content = "sessionid:" qstring :: number "/ r / n / r / n"; MakeMsNSLPMESSAGE (OK, Content); m_kopetetransfer = transfert; } void MSNP2P :: slotFileTransferRefused (const KopeteFileTransferInfo & info) {QStringList internalId = QStringList :: split ( ":", info.internalId ()); kdDebug (14140) << k_funcinfo << internalId << endl; if (internalId [0] .TOUINT () == m_sessionID) {m_branch = internalid [1]; m_callid = internalid [2]; Qstring content = "sessionid:" qstring :: number "/ r / n / r / n"; MakeMsNSLPMESSAGE (DECLINE, Content); } } Void msnp2p :: AbortcurrentTransfer () {if (m_kopetetransfer) {delete m_rfile; m_rfile = 0L; // this need to be reseted before sending the bye message. m_totaldatasize = 0; m_offset = 0; // fixme: I'm Not Sure It's LIKE THAT I Should Abort The Transfer. MakemsNSLpMessage (Bye, "/ R / N / R / N"); m_sessionID = 0; m_msgidentifier = 0;}} Void msnp2p :: slotkopetetransferdestroyed () {m_kopetetransfer = 0L; kddebug (14140) << k_funcinfo << endl;} #include "msnp2p.moc"