Ticket #193: webcam-firststeps.patch

File webcam-firststeps.patch, 56.4 kB (added by diederik, 4 months ago)

First steps to implement webcam, which I wrote a few weeks ago. Avoid keeping this on my harddrive..

  • network/applications/filetransferp2p.cpp

     
    473473            << ( usePreview ? "with thumbnail." : "without thumbnail." ); 
    474474#endif 
    475475 
    476   // Create the short name 
    477   uint shortNameLength = fileName_.length(); 
    478  
    479476  // Get the file data (preview 
    480477  ulong filesize  = fileData->size(); 
    481478  int flags       = (hasPreview ? 0 : 1); 
     
    493490  P2PMessage::insertBytes( context, flags,    16 );  // Field 4: 1 if NO preview data 
    494491 
    495492  // Field 5: the file name 
    496   const unsigned short * utf16Name = fileName_.utf16(); 
    497   int offset = 20; 
    498   for(uint i = 0; i < shortNameLength; ++i) 
    499   { 
    500     P2PMessage::insertShortBytes(context, utf16Name[i], offset); 
    501     offset += 2; 
    502   } 
    503  
     493  P2PMessage::insertUtf16String( context, fileName_, 20 ); 
    504494  P2PMessage::insertBytes(context, 0xFFFFFFFF, 570); // Field 6: some splitter field. 
    505495 
    506496  // Insert preview data 
     
    836826} 
    837827 
    838828 
     829 
    839830/** 
    840831 * @brief Called when the thumbnail is generated. 
    841832 */ 
     
    843834{ 
    844835  bool hasPreview = thumbnailProvider_->isSuccessful(); 
    845836 
    846   // Create the session id and context field 
    847   QString context   = createContextField( file_, hasPreview ); 
    848   uint    sessionID = (uint)KMessShared::generateID(); 
    849  
    850837  // Send the invitation 
    851   sendSlpSessionInvitation(sessionID, getAppId(), 2, context); 
     838  QString context = createContextField( file_, hasPreview ); 
     839  sendSlpSessionInvitation( KMessShared::generateID(), getAppId(), 2, context ); 
    852840 
    853  
    854841  // Generate the HTML to cancel the transfer 
    855842  QString html = i18n( "Sending file &quot;%1&quot; (%2).", 
    856843                       "<span class=\"filename invitationFilename\">" + fileName_ + "</span>", 
  • network/applications/p2papplicationbase.cpp

     
    286286    } 
    287287 
    288288    // Also attempt to match on the messageID alone if: 
    289     // - it's a 0x80 message. In this case sendP2PMessage() has overwritten the previous uniqueID. 
     289    // - it's a 0x80 message. In this case sendP2PMessageImpl() has overwritten the previous uniqueID. 
    290290    // - the UniqueID is not set. (also happens with normal ack messages in GAIM 1.5). 
    291291    // 
    292292    // This happens with 0x01 control messages: 
     
    863863      shouldSendAck_ = true; 
    864864      gotDataPreparation(p2pMessage); 
    865865    } 
    866     // Check for data messages 
    867     else if(p2pMessage.isMsnObjectData() 
    868          || p2pMessage.isFileData()) 
     866    // Check for normal data messages 
     867    else if( p2pMessage.isMsnObjectData() 
     868         ||  p2pMessage.isFileData()) 
    869869    { 
    870870      gotDataFragment(p2pMessage); 
    871871    } 
    872     else if(waitingState_ == P2P_WAIT_FOR_FILE_DATA 
    873          && p2pMessage.getFlags() == 0 
    874          && p2pMessage.isFragment()) 
     872    else if( waitingState_ == P2P_WAIT_FOR_FILE_DATA 
     873         &&  p2pMessage.getFlags() == 0 
     874         &&  p2pMessage.isFragment()) 
    875875    { 
    876876      // HACK: added for Kopete 0.9.2 code (has no flag set with data messages). 
    877877      kWarning() << "Expecting data message, " 
     
    882882                    " class="   << metaObject()->className() << ")!"; 
    883883      gotDataFragment(p2pMessage); 
    884884    } 
     885    else if( p2pMessage.getFlags() == 0 
     886         &&  p2pMessage.getDataSize() >= 18 
     887         &&  (quint8) ( p2pMessage.getData()[0] ) == 0x80 ) 
     888    { 
     889      // Webcam setup. Even WLM sends then without flags. 
     890      // At the switchboard the "footercode" is set to 4. 
     891      // Pass it to the WebcamTransferP2P class to deal with it. 
     892      // Not using gotDataFragment() here because it will check if everything is received. 
     893      gotData( p2pMessage ); 
     894    } 
    885895    else 
    886896    { 
    887897      // Unknown p2p message 
     
    11871197  // Reserve the next message ID. 
    11881198  nextMessageID_++; 
    11891199 
    1190   // Set the total size field, this causes sendP2PMessage() 
     1200  // Set the total size field, this causes sendP2PMessageImpl() 
    11911201  // to handle fragmented messages correctly. 
    11921202  // The 'fragmentMessageID_' is set so KMess sends all fragments with the same message ID. 
    11931203  // It could happen the client receives a SLP ACK meanwhile, and ACKs it with a new message ID. 
     
    12201230 
    12211231  // TODO: Check the various footer codes send for certain transfers. 
    12221232  QByteArray p2pMessage( 4, 0x00 ); 
    1223   sendP2PMessage(p2pMessage, 0, 1, P2P_MSG_DATA_PREPARATION); 
     1233  sendP2PMessageImpl( p2pMessage, 0, P2P_TYPE_PICTURE, P2P_MSG_DATA_PREPARATION ); 
    12241234 
    12251235  // TODO: after the data preparation is sent, WLM8 starts a direct connection setup: 
    12261236  // - it does not send an ACK yet 
     
    13731383              << " buffer=" << bytesRead; 
    13741384#endif 
    13751385 
    1376     // Determine the progress value now because mesageOffset_ it reset by the last sendP2PMessage() call. 
     1386    // Determine the progress value now because mesageOffset_ it reset by the last sendP2PMessageImpl() call. 
    13771387    unsigned long nextProgressValue = ( fragmentOffset_ + bytesRead ); 
    13781388 
    13791389    // Determine the flags: 
    13801390    // Funny, MSN6 doesn't seam to care which flag was set 
    13811391    // while transferring the file. KMess however, does. 
    1382     bool writeSuccess = false; 
     1392    uint flag = 0; 
    13831393    switch( dataType_ ) 
    13841394    { 
    1385       case P2P_TYPE_PICTURE: 
    1386         writeSuccess = sendP2PMessage( data, P2PMessage::MSN_FLAG_OBJECT_DATA, 1, P2P_MSG_DATA, fragmentMessageID_ ); 
    1387         break; 
    1388  
    1389       case P2P_TYPE_FILE: 
    1390         writeSuccess = sendP2PMessage( data, P2PMessage::MSN_FLAG_FILE_DATA, 2, P2P_MSG_DATA, fragmentMessageID_ ); 
    1391         break; 
    1392  
     1395      case P2P_TYPE_PICTURE: flag = P2PMessage::MSN_FLAG_OBJECT_DATA; break; 
     1396      case P2P_TYPE_FILE:    flag = P2PMessage::MSN_FLAG_FILE_DATA;   break; 
    13931397      default: 
    13941398        // Display error message for the first message only. 
    13951399        if( fragmentOffset_ == 0 ) 
     
    14011405                        " class="   << metaObject()->className() << 
    14021406                        " action=tryflag)."; 
    14031407        } 
    1404  
    1405         writeSuccess = sendP2PMessage( data, 0, 1, P2P_MSG_DATA, fragmentMessageID_ ); // The best solution in this situation 
    14061408    } 
    14071409 
     1410    // Send the message 
     1411    bool writeSuccess = sendP2PMessageImpl( data, flag, dataType_, P2P_MSG_DATA, fragmentMessageID_ ); 
     1412 
    14081413    // Avoid next round if write was blocked already. 
    14091414    // This could happen if a direct connection can't write all data. 
    14101415    // The DirectConnectionBase class will automatically send the remaining parts, so don't worry about that here. 
     
    15241529 
    15251530 
    15261531/** 
    1527  * @brief Send a P2P ACK message. 
     1532 * @brief Internal function to send a P2P ACK message. 
    15281533 * 
    15291534 * This method is the internal implementation for sendP2PAck() and others. 
    15301535 * 
     
    16931698 
    16941699 
    16951700/** 
    1696  * @brief Sends a P2P Message. 
     1701 * @brief Sends a complete P2P message payload. 
    16971702 * 
     1703 * It splits the data message into chunks which fit in the individual P2P messages. 
     1704 * 
     1705 * If you need to send data, use sendData() instead. 
     1706 * This method is stream based, and also takes care of state changes. 
     1707 * 
     1708 * @param messageData    The message payload to send. 
     1709 * @param flagField      The value for the flag field in the P2P header. 
     1710 * @param footerCode     The message footer, which is appended when the message is sent over the switchboard. 
     1711 * @param messageType    The type of the message. This value is returned with gotAck(). 
     1712 */ 
     1713void P2PApplicationBase::sendP2PMessage(const QByteArray &messageData, int flagField, P2PDataType footerCode, P2PMessageType messageType) 
     1714{ 
     1715  // Make sure the message gets sent in chunks of 1202 bytes. 
     1716  // Only set fragmentTotalSize_ if there are chunks, for useless debug messages. 
     1717  int remainingBytes = messageData.size(); 
     1718  fragmentOffset_    = 0; 
     1719  fragmentTotalSize_ = ( remainingBytes <= 1202 ? 0 : remainingBytes ); 
     1720 
     1721  do 
     1722  { 
     1723    // Let another QByteArray encapsulate a part of the utf8Message 
     1724    const char *dataPointer = messageData.data() + fragmentOffset_; 
     1725    uint        dataSize    = qMin( remainingBytes, 1202 ); 
     1726    QByteArray  messagePart = QByteArray::fromRawData( dataPointer, dataSize ); 
     1727 
     1728    // Send the message with the generic sendP2PMessageImpl() method 
     1729    // fragmentOffset_ is updated automatically 
     1730    sendP2PMessageImpl( messagePart, flagField, footerCode, messageType ); 
     1731    remainingBytes -= dataSize; 
     1732 
     1733#ifdef KMESSTEST 
     1734    KMESS_ASSERT( (uint) remainingBytes == fragmentTotalSize_ - fragmentOffset_ ); 
     1735#endif 
     1736  } 
     1737  while( remainingBytes > 0 ); 
     1738 
     1739  // Reset for normal operations 
     1740  fragmentOffset_    = 0; 
     1741  fragmentTotalSize_ = 0; 
     1742} 
     1743 
     1744 
     1745 
     1746/** 
     1747 * @brief Internal function to send a single P2P message. 
     1748 * 
    16981749 * This method constructs the P2P binary header fields. 
    16991750 * The constructed message is delivered to ApplicationList::sendMessage(). 
    17001751 * 
     
    17051756 * - outgoingMessages_ is updated to trace ACKs back later. 
    17061757 * 
    17071758 * The fragmentTotalSize_ should be set to the total size before sending message fragments. 
    1708  * This is done automatically by sendSlpMessage(). 
     1759 * This is done automatically by sendP2PMessage() function, which is part of the public API. 
    17091760 * 
    17101761 * @param messageData    The message payload to send. This can be binary file data or a SLP MIME data. 
    17111762 * @param flagField      The value for the flag field in the P2P header. 
    17121763 * @param footerCode     The message footer, which is appended when the message is sent over the switchboard. 
    17131764 * @param messageType    The type of the message. This value is stored with in the unacked message queue. 
    17141765 *                       This value is used later to respond to ACKs of certain special messages. 
    1715  * @param messageID      Optional message ID to enforce. If nothing is selected, the next available message ID will be used automatically. 
     1766 * @param messageID      Optional message ID to enforce. If nothing is selected, 
     1767 *                       the next available message ID will be used automatically. 
     1768 *                       This parameter is used to send file data with a specific message ID. 
    17161769 * @return               Whether the message can be sent. If the socket would block, false is returned. 
    17171770 *                       This only occurs for direct connections connections. 
    17181771 */ 
    1719 bool P2PApplicationBase::sendP2PMessage(const QByteArray &messageData, int flagField, uint footerCode, P2PMessageType messageType, unsigned long messageID) 
     1772bool P2PApplicationBase::sendP2PMessageImpl(const QByteArray &messageData, int flagField, P2PDataType footerCode, P2PMessageType messageType, unsigned long messageID) 
    17201773{ 
    17211774#ifdef KMESSTEST 
    17221775  KMESS_ASSERT( messageData.size() <= 1202 ); 
     
    17621815 
    17631816 
    17641817  // Determine the message size: 
     1818  // Internal trick: fragmentTotalSize_ is set by the caller if this is a splitted message. 
    17651819  uint messageSize = messageData.size(); 
    17661820  ulong totalSize; 
    17671821  ulong offsetField; 
     
    17861840  // Set session ID to zero when SLP messages are sent. 
    17871841  unsigned long sessionID = getSessionID(); 
    17881842  if( flagField == 0 
    1789   &&  messageType != P2P_MSG_DATA_PREPARATION ) 
     1843  &&  messageType != P2P_MSG_DATA_PREPARATION 
     1844  &&  messageType != P2P_MSG_WEBCAM_SETUP )  // HACK!  to fix this we need to pass messages as 'P2PMessage'. 
    17901845  { 
    17911846    sessionID = 0; 
    17921847  } 
     
    18041859 
    18051860 
    18061861  // Deliver the message over the correct link (switchboard or direct connection) 
    1807   bool writeSuccess = applicationList_->sendMessage( this, header, messageData, footerCode ); 
     1862  bool writeSuccess = applicationList_->sendMessage( this, header, messageData, (uint) footerCode ); 
    18081863  if( ! writeSuccess ) 
    18091864  { 
    18101865    // This only happens when a direct connection transfer is congested. 
     
    18961951 */ 
    18971952void P2PApplicationBase::sendP2PWaitingError() 
    18981953{ 
    1899   sendP2PMessage(0, P2PMessage::MSN_FLAG_WAITING, 0); 
     1954  sendP2PMessageImpl( 0, P2PMessage::MSN_FLAG_WAITING ); 
    19001955  shouldSendAck_ = false; 
    19011956} 
    19021957 
     
    19051960/** 
    19061961 * @brief Send a complete SLP message in multiple P2P packets. 
    19071962 * 
    1908  * It splits the SLP message into chunks which fit in the P2P payload. 
    1909  * Each part will be sent with sendP2PMessage(). 
    1910  * The messages always have a Session ID of zero. 
    1911  * 
    19121963 * This is a low-level function which is primary used by  
    19131964 * P2PApplication::sendSlpInvitation(), P2PApplication::sendSlpBye(), etc.. 
    19141965 * 
     1966 * It converts the SLP string to the payload data, 
     1967 * which could be splitted across multiple P2P messages. 
     1968 * 
    19151969 * @param  slpMessage   The whole SLP message to send, including SLP headers. 
    1916  * @param  messageType  The type of the message, used to trace ACK messages back later. 
     1970 * @param  messageType  The type of the message. This value is returned with gotAck(). 
    19171971 */ 
    19181972void P2PApplicationBase::sendSlpMessage(const QString &slpMessage, P2PMessageType messageType) 
    19191973{ 
     
    19281982  // Make sure the extra padded '\0' character is there. MSN 6-WLM rely on this. 
    19291983  utf8Message.append('\0'); 
    19301984 
    1931   // Make sure the message gets sent in chunks of 1202 bytes. 
    1932   // Only set fragmentTotalSize_ if there are chunks, for useless debug messages. 
    1933   int remainingBytes = utf8Message.size(); 
    1934   fragmentOffset_    = 0; 
    1935   fragmentTotalSize_ = ( remainingBytes <= 1202 ? 0 : remainingBytes ); 
    1936  
    1937   do 
    1938   { 
    1939     // Let another QByteArray encapsulate a part of the utf8Message 
    1940     char *dataPointer = utf8Message.data() + fragmentOffset_; 
    1941     uint  dataSize    = qMin(remainingBytes, 1202); 
    1942     messagePart = QByteArray::fromRawData( dataPointer, dataSize ); 
    1943  
    1944     // Send the message with the generic sendP2PMessage() method 
    1945     // fragmentOffset_ is updated automatically 
    1946     sendP2PMessage(messagePart, 0, 0, messageType); 
    1947     remainingBytes -= dataSize; 
    1948  
    1949 #ifdef KMESSTEST 
    1950     KMESS_ASSERT( (uint) remainingBytes == fragmentTotalSize_ - fragmentOffset_ ); 
    1951 #endif 
    1952   } 
    1953   while(remainingBytes > 0); 
    1954  
    1955   // Reset for normal operations 
    1956   fragmentOffset_    = 0; 
    1957   fragmentTotalSize_ = 0; 
     1985  sendP2PMessage( utf8Message, 0, P2P_TYPE_NEGOTIATION, messageType );  // will split the message. 
    19581986} 
    19591987 
    19601988 
     
    21372165      } 
    21382166 
    21392167      // Send the message 
    2140       sendP2PMessage(0, waitFlag, 0);  // TODO: pass the original message which is unacked!! 
     2168      sendP2PMessageImpl(0, waitFlag, 0);  // TODO: pass the original message which is unacked!! 
    21412169*/ 
    21422170      testUnAckedMessages( true );  // TODO: argument is not used, and replaces sendP2PWaitingError(). 
    21432171 
  • network/applications/webapplicationp2p.cpp

     
    8282//  int realAppID    = appID.mid(4, 4).toInt(); 
    8383 
    8484  // Parse the context 
    85   context += "===="; // Make sure the base64 encoded string is null padded to avoid problems with QString::fromUtf16(). 
     85  // Contents is something like '10331021;1;Tic Tac Toe' 
    8686  QByteArray decodedContext = QByteArray::fromBase64( context.toLatin1() ); 
     87  QString    contextString  = QString::fromUtf16( reinterpret_cast<const ushort*>( decodedContext.data() ), decodedContext.size() / 2 ); 
    8788 
    88   // Contents is something like '10331021;1;Tic Tac Toe' 
    89   QString contextString = QString::fromUtf16( reinterpret_cast<const ushort*>( decodedContext.data() ), decodedContext.size() / 2 ); 
    90  
    9189  // Get the fields 
    9290  QString unknown = contextString.section(';', 1, 1); 
    9391  QString appName = contextString.section(';', 2, 2); 
  • network/applications/p2papplication.cpp

     
    27872787 
    27882788#ifdef KMESSTEST 
    27892789  // Test, as other invocations likely result in undefined client behavour 
    2790   // ( 
    27912790  KMESS_ASSERT(   gotSlpMessage_ ); 
    2792   KMESS_ASSERT( isWaitingState( P2P_WAIT_DEFAULT ) ); 
     2791  KMESS_ASSERT( isWaitingState( P2P_WAIT_DEFAULT ) || isWaitingState( P2P_WAIT_USER_ACCEPT ) ); 
    27932792  KMESS_ASSERT( ! callID_.isEmpty() ); 
    27942793  KMESS_ASSERT( ! branch_.isEmpty() ); 
    27952794#endif 
  • network/applications/p2papplicationbase.h

     
    228228      P2P_MSG_DATA                = 5,  ///< The actual data message. 
    229229      P2P_MSG_SESSION_BYE         = 6,  ///< The SLP BYE message. 
    230230      P2P_MSG_TRANSFER_DECLINE    = 7,  ///< The SLP 603 Decline message. 
    231       P2P_MSG_SLP_ERROR           = 8   ///< One of the possible SLP error messages. 
     231      P2P_MSG_SLP_ERROR           = 8,  ///< One of the possible SLP error messages. 
     232      P2P_MSG_WEBCAM_SETUP        = 8   ///< One of the possible webcam setup messages. 
    232233    }; 
    233234 
    234235    /** 
     
    242243    , P2P_TYPE_PICTURE     = 1  ///< Packet contains MsnObject data. 
    243244    , P2P_TYPE_FILE        = 2  ///< Packet contains file data. 
    244245    , P2P_TYPE_INK         = 3  ///< Packet contains an Ink message. 
     246    , P2P_TYPE_WEBCAM      = 4  ///< Packet contains a webcam setup message. 
    245247    }; 
    246248 
    247249    /** 
     
    260262      P2P_WAIT_DEFAULT             = 0,  ///< Not waiting at all. 
    261263      P2P_WAIT_FOR_SLP_OK_ACK      = 1,  ///< Waiting for remote client to ack the the SLP OK message. 
    262264      P2P_WAIT_FOR_FILE_DATA       = 2,  ///< Waiting for remote client to send file data. 
     265      P2P_WAIT_FOR_WEBCAM_DATA     = 19, ///< Waiting for remote client to send webcam invite data. 
    263266      P2P_WAIT_FOR_PREPARE         = 3,  ///< Waiting for remote client to send some prepare message. 
    264267      P2P_WAIT_FOR_PREPARE_ACK     = 4,  ///< Waiting for remote client to ack the data preparation. 
    265268      P2P_WAIT_FOR_SLP_BYE         = 5,  ///< Waiting for remote client to send the SLP BYE. 
     
    332335    void                   sendP2PAbort(); 
    333336    // Send an low-level ACK message for a received message if this is needed. 
    334337    bool                   sendP2PAck(); 
     338    // Send a low-level P2P data message 
     339    void                   sendP2PMessage(const QByteArray &messageData, int flagField = 0, P2PDataType footerCode = P2P_TYPE_NEGOTIATION, P2PMessageType messageType = P2P_MSG_UNKNOWN); 
    335340    // Send a low-level error message that the application is waiting for a certain message. 
    336341    void                   sendP2PWaitingError(); 
    337342    // Send a given string using sendP2PMessage() 
     
    384389    // Send a P2P ACK message 
    385390    void                   sendP2PAckImpl(int ackType = P2PMessage::MSN_FLAG_ACK, UnAckedMessage *originalMessageData = 0); 
    386391    // Send a P2P message 
    387     bool                   sendP2PMessage(const QByteArray &messageData, int flagField = 0, uint footerCode = 0, P2PMessageType messageType = P2P_MSG_UNKNOWN, unsigned long messageID = 0); 
     392    bool                   sendP2PMessageImpl(const QByteArray &messageData, int flagField = 0, P2PDataType footerCode = P2P_TYPE_NEGOTIATION, P2PMessageType messageType = P2P_MSG_UNKNOWN, unsigned long messageID = 0); 
    388393    // Test if there are still unacked messages. 
    389394    void                   testUnAckedMessages(bool sendError); 
    390395 
  • network/applications/webcamtransferp2p.cpp

     
    1818#include "webcamtransferp2p.h" 
    1919 
    2020#include "../../kmessdebug.h" 
     21#include "../../utils/kmessshared.h" 
    2122#include "../mimemessage.h" 
    2223 
    2324#include <KLocale> 
     25#include <stdlib.h>   // rand() 
    2426 
    2527 
     28// 
     29// I'd like to have many thanks to the Kopete developers here, 
     30// without them writing this code would have been a lot harder. 
     31// Especially since all webcam online docs seam to be gone. 
     32// 
    2633 
     34 
     35 
     36 
    2737/** 
    2838 * Constructor 
    2939 * 
     
    3141 */ 
    3242WebcamTransferP2P::WebcamTransferP2P(ApplicationList *applicationList) 
    3343: P2PApplication(applicationList) 
     44, isUserSender_(false) 
    3445{ 
    3546  setApplicationType( ChatMessage::TYPE_APPLICATION_WEBCAM ); 
    3647} 
     
    6374 * 
    6475 * @param  message  The invitation message 
    6576 */ 
    66 void WebcamTransferP2P::contactStarted1_ContactInvitesUser(const MimeMessage & /*message*/) 
     77void WebcamTransferP2P::contactStarted1_ContactInvitesUser(const MimeMessage & message) 
    6778{ 
    68 #ifdef KMESSDEBUG_WEBAPPLICATION_P2P 
     79#ifdef KMESSDEBUG_WEBCAMTRANSFER_P2P 
    6980  kDebug(); 
    7081#endif 
    7182 
    72 #if 0 
    73   QString  appID   = message.getValue("AppID"); 
    74   QString  eufGuid = message.getValue("EUF-GUID"); 
    75   QString  context = message.getValue("Context"); 
     83  // Read the values from the message 
     84  uint    appID   = message.getValue("AppID").toUInt(); 
     85  QString eufGuid = message.getValue("EUF-GUID"); 
     86  QString context = message.getValue("Context"); 
    7687 
    77   // Parse the context 
    78   context += "===="; // Make sure the base64 encoded string is null padded to avoid problems with QString::fromUtf16(). 
    79   QByteArray decodedContext; 
    80   KCodecs::base64Decode(context.utf8(), decodedContext); 
     88  if(appID != 4) 
     89  { 
     90    kWarning() << "Received unexpected AppID: " << appID << "."; 
    8191 
    82   // Contents is another GUID 
    83   QString contextString = QString::fromUtf16( reinterpret_cast<const unsigned short*>(decodedContext.data()) ); 
     92    // Wouldn't know what to do if the AppID is not 2, so send an 500 Internal Error back. 
     93    showEventMessage( i18n("The webcam invitation was cancelled. Bad data was received."), ChatMessage::CONTENT_APP_CANCELED, true ); 
     94    sendCancelMessage( CANCEL_ABORT ); 
     95    return; 
     96  } 
     97 
     98  // Contact can request a webcam from us, or share it's own webcam. 
     99  isUserSender_ = ( eufGuid == getPullAppId() ); 
     100 
     101  // Parse the context, it contains another GUI 
     102  QByteArray decodedContext = QByteArray::fromBase64( context.toLatin1() ); 
     103  webcamGuid_ = P2PMessage::extractUtf16String( decodedContext.data(), 0, decodedContext.size() ); 
     104 
     105  // Display the accept message. 
     106#ifdef KMESSDEBUG_WEBCAMTRANSFER_P2P 
     107  kDebug() << "Webcam invitation received, GUID=" << webcamGuid_ << endl; 
     108  kDebug() << "waiting for user to accept..." << endl; 
    84109#endif 
    85110 
    86   // KMess does not support the webapplication invitations yet, 
    87   // this class is currently a STUB to produce a proper error message 
    88   showSystemMessage( i18n( "The contact is inviting you for '%1', but this is not implemented yet.", 
    89                            i18n("webcam") ), 
    90                      ChatMessage::CONTENT_SYSTEM_NOTICE, true ); 
     111  offerAcceptOrReject( i18n("You are invited to view this person's webcam.") ); 
     112} 
    91113 
    92   // Tell the contact we don't support this. 
    93   sendCancelMessage(CANCEL_NOT_INSTALLED); 
    94114 
    95 //  // Everything seams OK, accept this message: 
    96 //  contactStarted2_UserAccepts(); 
     115 
     116/** 
     117 * Step two of a contact-started chat: the user accepts 
     118 */ 
     119void WebcamTransferP2P::contactStarted2_UserAccepts() 
     120{ 
     121#ifdef KMESSDEBUG_FILETRANSFER_P2P 
     122  kDebug() << "sending accept message"; 
     123#endif 
     124 
     125  // Create the message 
     126  MimeMessage message; 
     127  message.addField( "SessionID", QString::number( getInvitationSessionID() ) ); 
     128 
     129  // Send the message 
     130  sendSlpOkMessage(message); 
     131  setWaitingState( P2P_WAIT_FOR_WEBCAM_DATA, 120000 );  // 120 seconds 
    97132} 
    98133 
    99134 
    100135 
     136 
    101137/** 
     138 * Step three of a contact-started chat: the contact confirms the accept 
     139 */ 
     140void WebcamTransferP2P::contactStarted3_ContactConfirmsAccept(const MimeMessage& /*message*/) 
     141{ 
     142#ifdef KMESSDEBUG_WEBCAMTRANSFER_P2P 
     143  kDebug(); 
     144#endif 
     145 
     146  // The invitation continues with the gotData() call. 
     147} 
     148 
     149 
     150 
     151/** 
     152 * Create the producer or viewer XML tag. 
     153 */ 
     154QString WebcamTransferP2P::createSipXml( uint session, uint rid ) 
     155{ 
     156  QString rootNode = ( isUserSender_ ? "producer" : "viewer" ); 
     157 
     158  QString ipTags = "<tcpipaddress1>10.0.0.1</tcpipaddress1>"; 
     159 
     160  /* 
     161  uint ip_number=1; 
     162  QStringList::iterator it; 
     163  QStringList ips=m_dispatcher->localIp(); 
     164  for ( it = ips.begin(); it != ips.end(); ++it ) 
     165  { 
     166          ip+=QString("<tcpipaddress%1>%2</tcpipaddress%3>").arg(ip_number).arg(*it).arg(ip_number); 
     167          ++ip_number; 
     168  } 
     169 
     170  //QString port = QString::number(getAvailablePort()); 
     171  //m_listener = new KServerSocket(port, this) ; 
     172  */ 
     173 
     174  int port = 0; 
     175 
     176  return "<" + rootNode + ">" 
     177           "<version>2.0</version>" 
     178           "<rid>" + QString::number( rid ) + "</rid>" 
     179           "<udprid>" + QString::number( rid + 1 ) + "</udprid>" 
     180           "<session>" + QString::number( session ) + "</session>" 
     181           "<ctypes>0</ctypes>" 
     182           "<cpu>2931</cpu>" 
     183           "<tcp>" 
     184             "<tcpport>7786</tcpport>"     // "\t\t\t\t\t\t\t\t  " 
     185             "<tcplocalport>7786</tcplocalport>"  // "\t\t\t\t\t\t\t\t  " 
     186             "<tcpexternalport>7786</tcpexternalport>" + ipTags + 
     187           "</tcp>" 
     188           "<udp>" 
     189             "<udplocalport>7786</udplocalport>" 
     190             "<udpexternalport>31863</udpexternalport>" 
     191             "<udpexternalip>" + ipTags + "</udpexternalip>" 
     192             "<a1_port>31859</a1_port>" 
     193             "<b1_port>31860</b1_port>" 
     194             "<b2_port>31861</b2_port>" 
     195             "<b3_port>31862</b3_port>" 
     196             "<symmetricallocation>1</symmetricallocation>" 
     197             "<symmetricallocationincrement>1</symmetricallocationincrement>" 
     198             "<udpversion>1</udpversion>" 
     199             "<udpinternalipaddress1>127.0.0.1</udpinternalipaddress1>" 
     200           "</udp>" 
     201           "<codec></codec>" 
     202           "<channelmode>1</channelmode>" 
     203         "</" + rootNode + ">\r\n\r\n"; 
     204} 
     205 
     206 
     207 
     208/** 
     209 * Return the application's GUID for "video conference" invitations. 
     210 */ 
     211QString WebcamTransferP2P::getConferenceAppId() 
     212{ 
     213  return "{4BD96FC0-AB17-4425-A14A-439185962DC8}"; 
     214} 
     215 
     216 
     217 
     218/** 
    102219 * Return the application's GUID for "user push" invitations. 
    103220 */ 
    104221QString WebcamTransferP2P::getPushAppId() 
     
    117234} 
    118235 
    119236 
     237 
     238/** 
     239 * Called when SIP data is received. 
     240 * This is the second stage of the webcam invitation. 
     241 * 
     242 * @param  message  P2P message with the data. 
     243 */ 
     244void WebcamTransferP2P::gotData(const P2PMessage &message) 
     245{ 
     246  bool contactStarted = ( ! isUserStartedApp() ); 
     247  QString contents = P2PMessage::extractUtf16String( message.getData(), 10, message.getDataSize() - 10 ); 
     248 
     249#ifdef KMESSDEBUG_FILETRANSFER_P2P 
     250  kDebug() << "received setup data:" << contents; 
     251#endif 
     252 
     253  sendP2PAck(); 
     254 
     255  // Find out what to do. 
     256  if( contents == "syn" ) 
     257  { 
     258    if( contactStarted ) 
     259    { 
     260      sendSipMessage("syn", 0x17,0x2a,0x01); 
     261    } 
     262    else 
     263    { 
     264      sendSipMessage("ack", 0xea,0x00,0x00); 
     265    } 
     266  } 
     267  else if( contents == "ack" ) 
     268  { 
     269    if( contactStarted ) 
     270    { 
     271      sendSipMessage("ack", 0xea,0x00,0x00); 
     272    } 
     273 
     274    // Send the <producer> message 
     275    if( isUserSender_ ) 
     276    { 
     277      uint session    = rand() % 1000 + 5000; 
     278      uint rid        = rand() % 100 + 50; 
     279      authentication_ = "recipientid=" + QString::number( rid ) + "&sessionid=" + QString::number( session ) + "\r\n\r\n"; 
     280      QString producerXml = createSipXml( session, rid ); 
     281      sendSipMessage( producerXml, 0, 0, 0 ); 
     282    } 
     283  } 
     284  else if( contents.startsWith("<") ) 
     285  { 
     286    // XML. 
     287  } 
     288  else 
     289  { 
     290    sendP2PAbort(); 
     291  } 
     292} 
     293 
     294 
     295 
     296/** 
     297 * Send a webcam negotiation message