enhydraboy

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

找到一份关于MSN P2P协议的转载。赶快保存下来,还有一个,URL已经无效了。

/*
    msnp2p.cpp - msn p2p protocol

    Copyright (c) 2003 by Olivier Goffart        <ogoffart@tiscalinet.be>

    *************************************************************************
    *                                                                       *
    * 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.                                   *
    *                                                                       *
    *************************************************************************
*/


#include "msnp2p.h"

#include <stdlib.h>

// qt
#include <qregexp.h>
#include <qfile.h>

// kde
#include <kdebug.h>
#include <kmdcodec.h>
#include <ktempfile.h>
#include <krun.h>
#include <klocale.h>
#include <kglobal.h>
#include <kdeversion.h>
#include <kstandarddirs.h>


MSNP2P::MSNP2P( QObject *parent , const char * name )
: QObject( parent , name )
{
 m_file=0l;
 m_Sfile=0L;
 m_msgIdentifier=0;
 m_sessionId=0;
 m_totalDataSize=0;
 m_offset=0;
}

MSNP2P::~MSNP2P()
{
 delete m_file;
 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-z0-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 < msg.size()-2)
  {
   if( msg.data()[startBinHeader]=='\r')
    startBinHeader++;
   if( msg.data()[startBinHeader]=='\n' )
   {
    if(justCR) break;
    else justCR=true;
   }
   else justCR=false;
   startBinHeader++;
  }
  startBinHeader++;
  if(!justCR || startBinHeader+48 > msg.size()) return;  //no binary header, or not long enough   , TODO: error handling

  //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)(unsigned char)(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)(unsigned char)(msg.data()[startBinHeader+8]) + (int)((unsigned char)msg.data()[startBinHeader+9])*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)(unsigned char)(msg.data()[startBinHeader+28])  << endl;
   return;
  }

  if(msg.size() < startBinHeader+48+dataMessageSize)
  {
   //the message's size is shorter than the announced size
   //TODO error handling
   return;
  }

  QString dataMessage=QCString((msg.data()+startBinHeader+48) , dataMessageSize);

  if(m_msgHandle.isEmpty())
  { //if these addresses were not previously set, get it, they should be provided in the first message at last.
   QRegExp rx("To: <msnmsgr:([^>]*)>");
   if( rx.search( dataMessage ) != -1 )
    m_myHandle=rx.cap(1);

   rx=QRegExp("From: <msnmsgr:([^>]*)>");
   if( rx.search( dataMessage ) != -1 )
    m_msgHandle=rx.cap(1);
  }

  //Send the ack if needed
  if(dataOffset+dataMessageSize>=totalSize)
   sendP2PAck( (msg.data()+startBinHeader) );

 

  if(m_file)  //we are already downloading something to this file
  {
   m_file->file()->writeBlock( (msg.data()+startBinHeader+48) , dataMessageSize );

   if(dataOffset+dataMessageSize >= totalSize) //the file is complete
   {
    m_file->close();

/*    //TODO: show the file in a better way
    #if KDE_IS_VERSION(3,1,90)
    KRun::runURL( m_file->name(), "image/png", true );
    #else
    KRun::runURL( m_file->name(), "image/png" );
    #endif

    delete m_file;*/
    emit fileReceived(m_file , m_obj);
    m_file=0;

    //send the bye message
    QCString dataMessage= QString(
      "BYE MSNMSGR:"+m_msgHandle+"MSNSLP/1.0\r\n"
      "To: <msnmsgr:"+m_msgHandle+">\r\n"
      "From: <msnmsgr:"+m_myHandle+">\r\n"
      "Via: MSNSLP/1.0/TLP ;branch={A0D624A6-6C0C-4283-A9E0-BC97B4B46D32}\r\n"
       "CSeq: 0\r\n"
       "Call-ID: {"+m_CallID.upper()+"}\r\n"
       "Max-Forwards: 0\r\n"
       "Content-Type: application/x-msnmsgr-sessionclosebody\r\n"
       "Content-Length: 3\r\n\r\n" ).utf8();
    sendP2PMessage(dataMessage);

    //deleteLater();
   }
  }
  else
  {
   kdDebug(14141) << "MSNP2P::slotReadMessage: dataMessage: "  << dataMessage << endl;

   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);
   }
      else if (dataMessage.contains("INVITE"))
   {
    //Parse the message to get some info for replying with the 200 OK message
    QRegExp rx(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
    rx.search( dataMessage );
    QString branch=rx.cap(1);

    rx=QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
    rx.search( dataMessage );
    QString callid=rx.cap(1);

    rx=QRegExp("SessionID: ([0-9]*)\r\n");
    rx.search( dataMessage );
    unsigned long int sessID=rx.cap(1).toUInt();

    rx=QRegExp("AppID: ([0-9]*)\r\n");
    rx.search( dataMessage );
    unsigned long int AppID=rx.cap(1).toUInt();

    if(AppID==1) //Ask for a msn picture, or emoticon,  currently, we always send the display picture
    {

     QString content="SessionID: " + QString::number( sessID ) ;

     QCString dataMessage= QString(
       "MSNSLP/1.0 200 OK\r\n"
       "To: <msnmsgr:"+m_msgHandle+">\r\n"
       "From: <msnmsgr:"+m_myHandle+">\r\n"
       "Via: MSNSLP/1.0/TLP ;branch={"+ branch +"}\r\n"
       "CSeq: 1\r\n"
       "Call-ID: {"+callid+"}\r\n"
       "Max-Forwards: 0\r\n"
       "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
       "Content-Length: "+ QString::number(content.length()+5)+"\r\n"
       "\r\n" + content + "\r\n\r\n").utf8(); //\0

     sendP2PMessage(dataMessage);

     //send the data preparation message
     m_sessionId=sessID;
     QByteArray initM(4);
     initM.fill('\0');
     sendP2PMessage(initM);

     //prepare to send the file
     m_Sfile = new QFile( locateLocal( "appdata", "msnpicture-"+ m_myHandle.lower().replace(QRegExp("[./~]"),"-")  +".png" ) );
     if(!m_Sfile->open(IO_ReadOnly))  {/*error?*/}
     m_totalDataSize=  m_Sfile->size();

     QTimer::singleShot( 10, this, SLOT(slotSendData()) ); //Go for upload
    }
    else
    {
     QCString dataMessage= QString(
       "MSNSLP/1.0 500 Internal Error\r\n"
       "To: <msnmsgr:"+m_msgHandle+">\r\n"
       "From: <msnmsgr:"+m_myHandle+">\r\n"
       "Via: MSNSLP/1.0/TLP ;branch={"+ branch +"}\r\n"
       "CSeq: 1\r\n"
       "Call-ID: {"+callid+"}\r\n"
       "Max-Forwards: 0\r\n"
       "Content-Type: null\r\n"
       "Content-Length: 0\r\n\r\n").utf8(); //\0
     sendP2PMessage(dataMessage);
    }
   }
   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()%0xFFFFFF00+4;
 QString 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((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); ;

 QString content="EUF-GUID: {A4268EEC-FEC5-49E5-95C3-F126696BDBF6}\r\n"
    "SessionID: "+ QString::number(sessID)+"\r\n"
    "AppID: 1\r\n"
    "Context: "  + msnObject;

 QCString dataMessage= QString(
   "INVITE MSNMSGR:"+ msgHandle + "  MSNSLP/1.0\r\n"
    "To: <msnmsgr:"+msgHandle+">\r\n"
    "From: <msnmsgr:"+myHandle+">\r\n"
   "Via: MSNSLP/1.0/TLP ;branch={"+branch.upper()+"}\r\n"
   "CSeq: 0\r\n"
   "Call-ID: {"+m_CallID.upper()+"}\r\n"
    "Max-Forwards: 0\r\n"
    "Content-Type: application/x-msnmsgr-sessionreqbody\r\n"
   "Content-Length: "+ QString::number(content.length()+5)+"\r\n"
   "\r\n" + content + "\r\n\r\n").utf8(); //\0

 sendP2PMessage(dataMessage);
}


void MSNP2P::sendP2PMessage(const QByteArray &dataMessage)
{
 if(m_sessionId == 0)
  kdDebug(14141) << k_funcinfo << QCString(dataMessage.data() , dataMessage.size()) << endl;

 QCString messageHeader=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
 binHeader[0]=(char)(m_sessionId%256);
 binHeader[1]=(char)((unsigned long int)(m_sessionId/256)%256);
 binHeader[2]=(char)((unsigned long int)(m_sessionId/(256*256))%256);
 binHeader[3]=(char)((unsigned long int)(m_sessionId/(256*256*256))%256);

 //MessageID
 binHeader[4]=(char)(m_msgIdentifier%256);
 binHeader[5]=(char)((unsigned long int)(m_msgIdentifier/256)%256);
 binHeader[6]=(char)((unsigned long int)(m_msgIdentifier/(256*256))%256);
 binHeader[7]=(char)((unsigned long int)(m_msgIdentifier/(256*256*256))%256);

 //offset
 binHeader[8]=(char)(m_offset%256);
 binHeader[9]=(char)((unsigned long int)(m_offset/256)%256);
 binHeader[10]=(char)((unsigned long int)(m_offset/(256*256))%256);
 binHeader[11]=(char)((unsigned long int)(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)((unsigned long int)(m_totalDataSize/256)%256);
  binHeader[18]=(char)((unsigned long int)(m_totalDataSize/(256*256))%256);
  binHeader[19]=(char)((unsigned long int)(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( messageHeader.length() + binHeader.size() + dataMessage.size() + 4 );
 for(unsigned int f=0; f< messageHeader.length() ; f++)
  data[f]=messageHeader[f];
 for(unsigned int f=0; f< binHeader.size() ; f++)
  data[messageHeader.length()+f]=binHeader[f];
 for(unsigned int f=0; f< dataMessage.size() ; f++)
  data[messageHeader.length()+binHeader.size()+f]=dataMessage[f];
 for(unsigned int f=0; f< 4 ; f++) //footer
  data[messageHeader.length()+binHeader.size()+dataMessage.size()+f]='\0';

 if(m_sessionId!=0)
 { //then, the footer ends with \1
  data[messageHeader.length()+binHeader.size() + dataMessage.size()  +3 ]='\1';
 }

 //send the message
 emit sendCommand("MSG", "D" , true , data , true );
}

void MSNP2P::sendP2PAck( const char* originalHeader )
{

 QCString messageHeader=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

 //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]=(char)((unsigned long int)(m_msgIdentifier/256)%256);
 binHeader[6]=(char)((unsigned long int)(m_msgIdentifier/(256*256))%256);
 binHeader[7]=(char)((unsigned long int)(m_msgIdentifier/(256*256*256))%256);

 if(a)
  m_msgIdentifier-=4;

 //total size
 binHeader[16]=originalHeader[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];


 QByteArray data( messageHeader.length() + binHeader.size() + 4 );
 for(unsigned int f=0; f< messageHeader.length() ; f++)
  data[f]=messageHeader[f];
 for(unsigned int f=0; f< binHeader.size() ; f++) //if binHeader is a QCString, it ends with \0 , which is ok
  data[messageHeader.length()+f]=binHeader[f];
 for(unsigned int f=0; f< 4 ; f++)
  data[messageHeader.length()+binHeader.size() +f ]='\0';

 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 dataBA(bytesRead);
 for (  int f = 0; f < bytesRead; f++ )
  dataBA[f] = data[f];

// kdDebug(14140) << "MSNP2P::slotSendData: offset="  << m_offset << "  size=" << bytesRead << "   totalSize=" << m_totalDataSize << "     sent=" << m_offset+bytesRead <<   endl;

 sendP2PMessage(dataBA);

 if( m_totalDataSize == 0  ) //this has been reseted bacause the file is completely send
 {
//  kdDebug(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()) );
}

 


#include "msnp2p.moc"

posted on 2004-06-23 11:55  Enhydraboy  阅读(4368)  评论(3编辑  收藏  举报