Ticket #193: webcam-firststeps.patch

File webcam-firststeps.patch, 56.4 KB (added by diederik, 4 years 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 
     298 */ 
     299void WebcamTransferP2P::sendSipMessage( const QString &contents, quint8 flag1, quint8 flag2, quint8 flag3 ) 
     300{ 
     301  QByteArray sipMessage(10 + contents.length() * 2 + 2, '\0'); 
     302 
     303  sipMessage[0] = 0x80; 
     304  sipMessage[1] = flag1; 
     305  sipMessage[2] = flag2; 
     306  sipMessage[3] = flag3; 
     307  sipMessage[4] = 0x08; 
     308 
     309  P2PMessage::insertUtf16String( sipMessage, contents, 10 ); 
     310 
     311  // Send message. 
     312  sendP2PMessage( sipMessage, 0, P2P_TYPE_WEBCAM, P2P_MSG_WEBCAM_SETUP ); 
     313} 
     314 
     315 
     316 
     317/** 
     318 * Step one of a user-started chat: the user invites the contact 
     319 */ 
     320void WebcamTransferP2P::userStarted1_UserInvitesContact() 
     321{ 
     322#ifdef KMESSDEBUG_FILETRANSFER_P2P 
     323  kDebug() << "starting webcam session"; 
     324#endif 
     325 
     326  // Generate context field 
     327  webcamGuid_ = "{11CF1BE9-3186-93A5-B38D-914DB1163BF7}"; 
     328 
     329  // Write the string 
     330  int contextLength = webcamGuid_.size() * 2 + 2; 
     331  QByteArray context( contextLength, '\0' ); 
     332  P2PMessage::insertUtf16String( context, webcamGuid_, 0 ); 
     333 
     334  // Encode to base64 
     335  QString encodedContext = context.toBase64().replace("AAAA", "AA==");  // TODO: find out why the null padding is wrong. 
     336 
     337  encodedContext = "ewAwADQAMgA1AEUANwA5ADcALQA0ADkARgAxAC0ANABEADMANwAtADkAMAA5AEEALQAwADMAMQAxADEANgAxADEAOQBEADkAQgB9AA=="; 
     338 
     339  // Send the invitation 
     340  sendSlpSessionInvitation( KMessShared::generateID(), getPushAppId(), 4, encodedContext ); 
     341} 
     342 
     343 
     344 
     345/** 
     346 * Step two of a user-started chat: the contact accepts 
     347 */ 
     348void WebcamTransferP2P::userStarted2_ContactAccepts(const MimeMessage &message) 
     349{ 
     350 
     351} 
     352 
     353 
     354 
     355/** 
     356 * Step three of a user-started chat: the user prepares for the session 
     357 */ 
     358void WebcamTransferP2P::userStarted3_UserPrepares() 
     359{ 
     360 
     361 
     362} 
     363 
     364 
     365 
    120366#include "webcamtransferp2p.moc" 
  • network/applications/webcamtransferp2p.h

     
    4444    // The destructor 
    4545    virtual               ~WebcamTransferP2P(); 
    4646 
     47    // Return the application's GUID for "video conference" invitations. 
     48    static QString         getConferenceAppId(); 
     49 
    4750    // Return the application's GUID (for the "user push" invitation) 
    4851    static QString         getPushAppId(); 
    4952 
     
    5457 
    5558    // Step one of a contact-started chat: the contact invites the user 
    5659    void                   contactStarted1_ContactInvitesUser(const MimeMessage& message); 
     60    // Step two of a contact-started chat: the user accepts 
     61    void                   contactStarted2_UserAccepts(); 
     62    // Step three of a contact-started chat: the contact confirms the accept 
     63    void                   contactStarted3_ContactConfirmsAccept(const MimeMessage& message); 
     64    // Create the producer or viewer XML tag 
     65    QString                createSipXml(uint session, uint rid); 
     66    // Called when data is received 
     67    void                   gotData(const P2PMessage &message); 
     68    // Send a webcam negotiation message. 
     69    void                   sendSipMessage( const QString &contents, quint8 flag1, quint8 flag2, quint8 flag3 ); 
     70    // Step one of a user-started chat: the user invites the contact 
     71    void                   userStarted1_UserInvitesContact(); 
     72    // Step two of a user-started chat: the contact accepts 
     73    void                   userStarted2_ContactAccepts(const MimeMessage &message); 
     74    // Step three of a user-started chat: the user prepares for the session 
     75    void                   userStarted3_UserPrepares(); 
     76 
     77 
     78  private: 
     79    // The authentication string for the webcam connection 
     80    QString                authentication_; 
     81    // Whether the contact requests the webcam from us, or it wants to share it's webcam. 
     82    bool                   isUserSender_; 
     83    // The GUID received in the invitation 
     84    QString                webcamGuid_; 
    5785}; 
    5886 
    5987#endif 
  • network/p2pmessage.cpp

     
    103103  return ackDataSize_; 
    104104} 
    105105 
     106 
    106107// Retreives the nonce field (for direct connection handshake. 
    107108QString P2PMessage::getNonce() const 
    108109{ 
     
    258259  ackSessionID_    = extractBytes     ( messageData, 32); 
    259260  ackUniqueID_     = extractBytes     ( messageData, 36); 
    260261  ackDataSize_     = extractLongBytes ( messageData, 40); 
     262 
     263  // Verify data size! 
     264  if( (uint) message_.size() < ( 48 + dataSize_ ) )  // message size could also contain the footer code. 
     265  { 
     266    kWarning().nospace() << "corrupt data size header detected " 
     267                            "(header=48" 
     268                            " datasize=" << dataSize_ << 
     269                            " total=" << message_.size() << ")"; 
     270    dataSize_ = message_.size() - 48; 
     271  } 
    261272} 
    262273 
    263274 
     
    267278// ------------------------------------------------------ 
    268279// A utility to fill the byte data block 
    269280 
     281 
     282 
    270283void P2PMessage::insertBytes(QByteArray &buffer, const unsigned int value, const int offset) 
    271284{ 
    272285  // Also based on Kopete code. I started with a nice struct, 
     
    294307  // However, note the bytes are placed in network order. (big endian) 
    295308} 
    296309 
    297 void P2PMessage::insertShortBytes(QByteArray &buffer, const unsigned short value, const int offset) 
    298 { 
    299   buffer[offset + 0] = (char) ( value        & 0xFF); 
    300   buffer[offset + 1] = (char) ((value >>  8) & 0xFF); 
    301 } 
    302310 
     311 
    303312void P2PMessage::insertNonce(QByteArray &buffer, const QString &nonce, const int offset) 
    304313{ 
    305314  // Remove the separators 
     
    327336  } 
    328337} 
    329338 
     339 
     340 
     341// Copy sort integers into the binary header. 
     342void P2PMessage::insertUtf16String(QByteArray &buffer, const QString &value, int offset) 
     343{ 
     344  const unsigned short *utf16Value = value.utf16(); 
     345  uint valueLength = value.length(); 
     346  for(uint i = 0; i < valueLength; ++i) 
     347  { 
     348    const short utf16Char = utf16Value[i]; 
     349    buffer[offset + 0] = (char) ( utf16Char        & 0xFF); 
     350    buffer[offset + 1] = (char) ((utf16Char >>  8) & 0xFF); 
     351    offset += 2; 
     352  } 
     353} 
     354 
     355 
     356 
    330357unsigned int P2PMessage::extractBytes(const char *data, const int offset) 
    331358{ 
    332359  // Convert the bytes from network order to a normal int. 
     
    336363        | ((unsigned char) data[offset + 3] << 24)); 
    337364} 
    338365 
     366 
     367 
    339368unsigned long P2PMessage::extractLongBytes(const char *data, const int offset) 
    340369{ 
    341370  // Convert the bytes from network order to a long int. 
     
    354383} 
    355384 
    356385 
     386 
    357387QString P2PMessage::extractNonce(const char *data, const int offset) 
    358388{ 
    359389  const int noncePos[] = { 3,2,1,0            // Field 1 reversed 
     
    380410  return "{" + hex + "}"; 
    381411} 
    382412 
     413 
     414 
     415QString P2PMessage::extractUtf16String( const char *data, const int offset, int size ) 
     416{ 
     417  // Avoid copying the null char 
     418  if( data[ offset + size - 1 ] == '\0' 
     419  &&  data[ offset + size - 2 ] == '\0' ) 
     420  { 
     421#ifdef KMESSDEBUG_P2PMESSAGE 
     422    kDebug() << "avoid copying null character to utf16 string"; 
     423#endif 
     424    size--; 
     425  } 
     426 
     427  return QString::fromUtf16( reinterpret_cast<const ushort *>( data + offset ), size / 2 ); 
     428} 
     429 
  • network/p2pmessage.h

     
    104104 
    105105  // Copy integers into the binary header. 
    106106  static          void insertBytes(QByteArray& buffer, const unsigned int value, const int offset); 
    107   // Copy sort integers into the binary header. 
     107  // Copy short integers into the binary header. 
    108108  static          void insertShortBytes(QByteArray &buffer, const unsigned short value, const int offset); 
    109109  // Copy a nonce string into the binary header. 
    110110  static          void insertNonce(QByteArray& buffer, const QString &nonce, const int offset = 32); 
     111  // Copy short integers into the binary header. 
     112  static          void insertUtf16String(QByteArray &buffer, const QString &value, int offset); 
    111113  // Extracts the bytes from the data block. 
    112114  static unsigned int  extractBytes(const char *data, const int offset); 
    113115  // Extracts the bytes from the data block. 
    114116  static unsigned long extractLongBytes(const char *data, const int offset); 
    115117  // Extracts the nonce string from the data header. 
    116118  static QString       extractNonce(const char *data, const int offset = 32); 
     119  // Extracts an utf16 string from the data header. 
     120  static QString       extractUtf16String(const char *data, const int offset, int size); 
    117121 
    118122 
    119123private:    // Helper functions 
  • network/msnnotificationconnection.cpp

     
    495495#else 
    496496  // NOTE: When changing this property, all MSNP2P code need to be retested again!! 
    497497  // The capabilities are like a contract. The other client assumes everything works that's promised here. 
    498   const uint capabilities = ( Contact::MSN_CAP_MSN75 | Contact::MSN_CAP_WINKS | Contact::MSN_CAP_MULTI_PACKET | Contact::MSN_CAP_INK_GIF ); 
     498  const uint capabilities = ( Contact::MSN_CAP_MSN75 | Contact::MSN_CAP_WINKS | Contact::MSN_CAP_MULTI_PACKET | Contact::MSN_CAP_INK_GIF | Contact::MSN_CAP_VIDEO_CHAT ); 
    499499#endif 
    500500 
    501501  QString statusCode = MsnStatus::getCode( newStatus ); 
  • chat/chatmaster.h

     
    125125    void               slotSwitchboardReady(); 
    126126    // Start a netmeeting invitation 
    127127    void               startNetMeeting(const QString &handle); 
     128    // Start a webcam transfer with the contact 
     129    void               startWebcamTransfer( const QString &handle ); 
    128130 
    129131  private:  // private methods 
    130132    // Create and register a new switchboard 
  • chat/chatwindow.cpp

     
    493493 
    494494  // Create the actions 
    495495  sendAction_        = new KAction( KIcon("folder-remote"),                         i18n("Send a &File"),     this ); 
     496  webcamAction_      = new KAction( KIcon("webcamsend"),                            i18n("Webcam chat"),     this ); 
    496497  meetingAction_     = new KAction( KIcon("gnomemeeting"),                          i18n("Start a &Meeting"), this ); 
    497498  nudgeAction_       = new KAction( KIcon("preferences-desktop-notification-bell"), i18n("Send a &Nudge!"),   this ); 
    498499  saveAction         = new KAction( KIcon("document-save"),                         i18n("Save chat"),        this ); 
    499500  addEmoticonAction_ = new KAction( KIcon("emoticons"),                             i18n("&Emoticons"),       this ); 
     501  closeAllAction_    = new KAction( KIcon("dialog-close"),                          i18n("Close &All Tabs"),  this ); 
    500502  closeAction        = KStandardAction::close( this, SLOT( queryClose() ), actionCollection_ ); 
    501   closeAllAction_    = new KAction( KIcon("dialog-close"),                          i18n("Close &All Tabs"),  this ); 
    502503 
    503504  // Initially the chat has one tab only, so the Close All option is not useful 
    504505  closeAllAction_->setVisible( false ); 
     
    512513  inviteButton_->setDelayed( false ); 
    513514 
    514515  // Connect them 
    515   connect( sendAction_,        SIGNAL(         triggered(bool) ), 
    516            this,               SLOT  ( startFileTransfer()     ) ); 
    517   connect( nudgeAction_,       SIGNAL(         triggered(bool) ), 
    518            this,               SLOT  (         sendNudge()     ) ); 
    519   connect( meetingAction_,     SIGNAL(         triggered(bool) ), 
    520            this,               SLOT  (      startMeeting()     ) ); 
    521   connect( saveAction,         SIGNAL(         triggered(bool) ), 
    522            this,               SLOT  (          saveChat()     ) ); 
    523   connect( addEmoticonAction_, SIGNAL(         triggered()     ), 
    524            this,               SLOT  ( showEmoticonPanel()     ) ); 
    525   connect( closeAllAction_,    SIGNAL(         triggered()     ), 
    526            this,               SLOT  (     queryCloseAll()     ) ); 
     516  connect( sendAction_,        SIGNAL( triggered(bool) ), this, SLOT(   startFileTransfer() ) ); 
     517  connect( webcamAction_,      SIGNAL( triggered(bool) ), this, SLOT( startWebcamTransfer() ) ); 
     518  connect( nudgeAction_,       SIGNAL( triggered(bool) ), this, SLOT(           sendNudge() ) ); 
     519  connect( meetingAction_,     SIGNAL( triggered(bool) ), this, SLOT(        startMeeting() ) ); 
     520  connect( saveAction,         SIGNAL( triggered(bool) ), this, SLOT(            saveChat() ) ); 
     521  connect( addEmoticonAction_, SIGNAL( triggered()     ), this, SLOT(   showEmoticonPanel() ) ); 
     522  connect( closeAllAction_,    SIGNAL( triggered()     ), this, SLOT(       queryCloseAll() ) ); 
    527523 
    528524#ifdef HAS_KPHONE  // If KPhone is not enabled, don't support voice conversation 
    529525  KAction *conversation = new KAction( "kphone", i18n("Start or Stop a &Conversation"), this ); 
     
    534530  // The Invite menu goes before this separator 
    535531  firstChatMenuItem_ = chatMenu_->addSeparator(); 
    536532  chatMenu_->addAction( sendAction_        ); 
    537   chatMenu_->addAction( meetingAction_     ); 
     533  chatMenu_->addAction( webcamAction_      ); 
     534  chatMenu_->addAction( meetingAction_     );   // TODO: remove this. 
    538535#ifdef HAS_KPHONE  // If KPhone is not enabled, don't support voice conversation 
    539536  chatMenu_->addAction( conversation       ); 
    540537#endif   
     
    548545  // removed 'close', not a frequently used action (and you'll have Alt+F4 and the window close button for that). 
    549546  toolBar()->addAction( inviteButton_      ); 
    550547  toolBar()->addAction( sendAction_        ); 
     548  toolBar()->addAction( webcamAction_      ); 
    551549  toolBar()->addAction( nudgeAction_       ); 
    552550  toolBar()->addAction( addEmoticonAction_ ); 
    553551 
     
    569567 
    570568  // Create the "edit" menus 
    571569  changeFont      = new KAction( KIcon("preferences-desktop-font"), i18n("Change &Font"), this ); 
    572   changeFontColor = new KAction( KIcon("format-stroke-color"), i18n("Change Font &Color"), this ); 
    573   connect( changeFont,      SIGNAL(     triggered(bool) ), 
    574            this,              SLOT(      editFont()     ) ); 
    575   connect( changeFontColor, SIGNAL(     triggered(bool) ), 
    576            this,              SLOT( editFontColor()     ) ); 
     570  changeFontColor = new KAction( KIcon("format-stroke-color"),      i18n("Change Font &Color"), this ); 
     571  connect( changeFont,      SIGNAL( triggered(bool) ), this, SLOT(      editFont() ) ); 
     572  connect( changeFontColor, SIGNAL( triggered(bool) ), this, SLOT( editFontColor() ) ); 
    577573 
    578574  // Add shorter texts for the toolbar 
    579575  changeFont ->setIconText( i18n("&Font") ); 
     
    14671463 
    14681464 
    14691465 
     1466// Start a file transfer 
     1467void ChatWindow::startWebcamTransfer() 
     1468{ 
     1469  getCurrentChat()->startWebcamTransfer(); 
     1470} 
     1471 
     1472 
    14701473// Start a voice conversation 
    14711474void ChatWindow::startConversation() 
    14721475{ 
  • chat/chatwindow.h

     
    180180    void             startFileTransfer(); 
    181181    // Start GnomeMeeting with a contact. 
    182182    void             startMeeting(); 
     183    // Start a webcam transfer 
     184    void             startWebcamTransfer(); 
    183185    // Called when the "use emoticons" action is called. 
    184186    void             toggleEmoticons( bool useEmoticons ); 
    185187    // Called when the "show sidebar" action is called. 
     
    188190    void             toggleSpellCheck( bool useSpellCheck ); 
    189191 
    190192  private: // private attributes 
     193    // ActionCollection 
     194    KActionCollection *actionCollection_; 
    191195    // The add emoticon action 
    192196    KAction         *addEmoticonAction_; 
    193197    // A timer used to make the caption blink on new messages. 
     
    250254    QTimer           statusTimer_; 
    251255    // The bar of chat tabs 
    252256    KTabBar         *tabBar_; 
    253     // ActionCollection 
    254     KActionCollection *actionCollection_; 
     257    // Action to start a file transfer 
     258    KAction         *webcamAction_; 
    255259 
    256260  signals: // Public signals 
    257261    // Signal that the window is about to close. 
  • chat/chat.cpp

     
    12611261 
    12621262 
    12631263 
     1264// Send webcam data to a contact. 
     1265void Chat::startWebcamTransfer() 
     1266{ 
     1267  // Choose the contact. 
     1268  QString handle = chooseContact(); 
     1269 
     1270  if( KMESS_NULL(msnSwitchboardConnection_) ) 
     1271  { 
     1272#ifdef KMESSDEBUG_CHAT_GENERAL 
     1273    kWarning() << "Tried to open a meeting with \'" << handle << "\' without a switchboard!"; 
     1274#endif 
     1275    return; 
     1276  } 
     1277 
     1278  emit requestWebcamTransfer(handle); 
     1279} 
     1280 
     1281 
    12641282#include "chat.moc" 
  • chat/chat.h

     
    106106    void               startConversation(); 
    107107    // Start GnomeMeeting with a contact. 
    108108    void               startMeeting(); 
     109    // Send webcam data to a contact. 
     110    void               startWebcamTransfer(); 
    109111 
    110112  private: // Private methods 
    111113    // Check if the user enabled an auto-reply, and send it 
     
    166168    void               requestFileTransfer(const QString &handle, const QString &filename); 
    167169    // Signal that the ChatMaster should start a newmeeting session. 
    168170    void               requestNetMeeting(const QString &handle); 
     171    // Signal that the ChatMaster should start a webcam transfer. 
     172    void               requestWebcamTransfer(const QString &handle); 
    169173    // The user wants to add or remove a contact. 
    170174    void               setContactAdded( QString handle, bool isAdded ); 
    171175    // The user wants to allow a contact to see his/hers online status. 
  • chat/chatmaster.cpp

     
    2323#include "../contact/msnobject.h" 
    2424#include "../network/applications/applicationlist.h" 
    2525#include "../network/applications/mimeapplication.h" 
    26 #include "../network/applications/msnobjecttransferp2p.h" 
    2726#include "../network/applications/p2papplication.h" 
    2827#include "../network/applications/filetransfer.h" 
    2928#include "../network/applications/filetransferp2p.h" 
    3029#include "../network/applications/gnomemeeting.h" 
    3130#include "../network/applications/inktransferp2p.h" 
     31#include "../network/applications/msnobjecttransferp2p.h" 
     32#include "../network/applications/webcamtransferp2p.h" 
    3233#include "../network/chatmessage.h" 
    3334#include "../network/msnswitchboardconnection.h" 
    3435#include "../currentaccount.h" 
     
    14731474           this,    SLOT  (        startFileTransfer(const QString&,const QString&) ) ); 
    14741475  connect( newChat, SIGNAL(        requestNetMeeting(const QString&)                ), 
    14751476           this,    SLOT  (          startNetMeeting(const QString&)                ) ); 
     1477  connect( newChat, SIGNAL(    requestWebcamTransfer(const QString&)                ), 
     1478           this,    SLOT  (      startWebcamTransfer(const QString&)                ) ); 
    14761479  connect( newChat, SIGNAL(                  closing(QObject*)                      ), 
    14771480           this,    SLOT  (          slotChatClosing(QObject*)                      ) ); 
    14781481  connect( newChat, SIGNAL(                destroyed(QObject*)                      ), 
     
    16511654 
    16521655  if( contact != 0 && contact->hasP2PSupport() ) 
    16531656  { 
    1654     // The contact supports file transfer over MSNP2P 
    1655     P2PApplication *app = new FileTransferP2P(appList, filename); 
    1656     startApplication(app); 
    1657   } 
    1658   else 
    1659   { 
    16601657    // This should be so rare that it justifies a warning message. 
    16611658    if( contact == 0 ) 
    16621659    { 
     
    16701667    // The contact only supports file transfer the old way 
    16711668    MimeApplication *app = new FileTransfer(handle, filename); 
    16721669    startApplication(app); 
     1670    return; 
    16731671  } 
     1672 
     1673  // The contact supports file transfer over MSNP2P 
     1674  P2PApplication *app = new FileTransferP2P(appList, filename); 
     1675  startApplication(app); 
    16741676} 
    16751677 
    16761678 
     
    17611763 
    17621764 
    17631765 
     1766// Start a webcam transfer with the contact 
     1767void ChatMaster::startWebcamTransfer( const QString &handle ) 
     1768{ 
     1769  ApplicationList *appList = getApplicationList(handle); 
     1770  if(KMESS_NULL(appList)) return; 
     1771 
     1772  // Get the contact properties, see how we can transfer the file. 
     1773  const ContactBase *contact = CurrentAccount::instance()->getContactByHandle( handle ); 
     1774  if( contact == 0 || ! contact->hasP2PSupport() ) 
     1775  { 
     1776    kWarning() << "Contact" << handle << "does not support P2P transfers, using old MIME invitations instead."; 
     1777    return; 
     1778  } 
     1779 
     1780  // The contact supports file transfer over MSNP2P 
     1781  P2PApplication *app = new WebcamTransferP2P(appList); 
     1782  startApplication(app); 
     1783} 
     1784 
     1785 
     1786 
    17641787/** 
    17651788 * Periodically called method for update commands. 
    17661789 * 
  • kmessdebug.h

     
    8787 
    8888  #define KMESSDEBUG_MIMEMESSAGE 
    8989  #define KMESSDEBUG_MULTIPACKETMESSAGE 
     90  #define KMESSDEBUG_P2PMESSAGE 
    9091  #define KMESSDEBUG_APPLICATION 
    9192  #define KMESSDEBUG_APPLICATIONLIST 
    9293  #define KMESSDEBUG_MIMEAPPLICATION 
     
    9697  #define KMESSDEBUG_FILETRANSFER 
    9798  #define KMESSDEBUG_REMOTEDESKTOP 
    9899  #define KMESSDEBUG_WEBAPPLICATION_P2P 
     100  #define KMESSDEBUG_WEBCAMTRANSFER_P2P 
    99101 
    100102  #define KMESSDEBUG_UPNP 
    101103  #define KMESSDEBUG_SOAPMESSAGE 
  • dialogs/networkwindow.cpp

     
    1717 
    1818#include "../kmessdebug.h" 
    1919 
    20 #ifdef KMESS_NETWORK_WINDOW 
    21  
    2220#include "networkwindow.h" 
    2321 
    2422#include "../network/mimemessage.h" 
     
    457455 
    458456 
    459457  // Return a log description for the message 
    460   QString NetworkWindow::describeP2PMessage(const QByteArray &message, const int headerStart) 
     458  QString NetworkWindow::describeP2PMessage(const QByteArray &message, const int headerStart, int footerCode) 
    461459  { 
     460    Q_UNUSED(footerCode); 
     461 
    462462    // Prepare to extract certain P2P header fields 
    463463    QDataStream binaryStream( message ); 
    464464    binaryStream.setByteOrder( QDataStream::LittleEndian ); 
     
    504504          } 
    505505        } 
    506506 
     507        // Another awkward data message (webcam setup) 
     508        if( sessionId  != 0 
     509        &&  flags      == 0 
     510        &&  dataSize   >= 18 
     511        &&  (quint8) ( message[ headerStart + 48 ] ) == 0x80 ) 
     512        { 
     513          return formatDescription("Webcam setup message"); 
     514        } 
     515 
    507516        // No flags means the contents is a string with SLP-like mime fields, used to negotiate the session. 
    508517        // datasize of 0 is not handed here, is an invalid packet. 
    509518        // KMess does parse it as ACK message for compatibility with broken clients. 
     
    711720        else 
    712721        { 
    713722          // A p2p message from the direct connection 
    714           logMessage += formatP2PMessage(incoming, message, 0); 
    715           logMessage += describeP2PMessage(message, 0); 
     723          logMessage += formatP2PMessage(incoming, message, 0, 0); 
     724          logMessage += describeP2PMessage(message, 0, 0); 
    716725        } 
    717726        break; 
    718727 
     
    759768            // Extract the first utf-8 mime part, locate start of p2p header 
    760769            QString p2pMimePart = utf8Message.left(mimeEnd + 4); 
    761770            int     headerStart = p2pMimePart.toUtf8().size();  // convert mimeEnd index, utf-8 may have double characters 
     771            int     footerCode  = (int) message[ message.size() - 1 ];  // only take last byte for now. 
    762772 
    763773            // Add the mime part and binary p2p part 
    764774            logMessage += formatString( p2pMimePart ); 
    765             logMessage += formatP2PMessage(incoming, message, headerStart); 
    766             logMessage += describeP2PMessage(message, headerStart); 
     775            logMessage += formatP2PMessage(incoming, message, headerStart, footerCode); 
     776            logMessage += describeP2PMessage(message, headerStart, footerCode); 
    767777          } 
    768778        } 
    769779        else 
     
    821831 
    822832 
    823833  // Format a msnp2p message to be displayed. 
    824   QString NetworkWindow::formatP2PMessage(bool incoming, const QByteArray &message, const int headerStart) 
     834  QString NetworkWindow::formatP2PMessage(bool incoming, const QByteArray &message, const int headerStart, int footerCode) 
    825835  { 
     836    Q_UNUSED(footerCode); 
     837 
    826838    QString logMessage; 
    827839 
    828840    // Prepare to extract certain P2P header fields 
     
    862874    } 
    863875 
    864876    // Show header information 
    865     logMessage += formatRawData(incoming, message, headerStart, 48, 16); 
     877    logMessage += formatRawData(incoming, message, headerStart, 48, 4, 16); 
    866878 
    867879    // Add header debug details 
    868880    logMessage += "<font color='#333333'>"; 
     
    887899    logMessage += "</font>"; 
    888900 
    889901    // The message a utf-8 (MSNSLP) payload if the session id is zero. 
     902    const char *dataPtr = ( message.data() + p2pDataStart ); 
    890903    bool hasSlpPayload  = ( sessionId == 0 && dataSize > 0 ); 
    891     int  dataEnd        = ( p2pDataStart + dataSize ); 
    892     int  realEnd        = ( message.size() ); 
     904    int  safeEnd        = qMin( message.size(), (int) ( p2pDataStart + dataSize ) ); 
     905    int  safeSize       = qMin( (int) dataSize, ( message.size() - p2pDataStart ) ); 
    893906    if( hasSlpPayload ) 
    894907    { 
    895908      // See if the SLP body is terminated with a final null character (should be). 
    896       bool hasSlpNullEnd  = ( message[ dataEnd - 1 ] == 0x00 ); 
     909      int  slpLength     = safeSize; 
     910      bool hasSlpNullEnd = ( message[ p2pDataStart + slpLength - 1 ] == 0x00 ); 
    897911      if( hasSlpNullEnd ) 
    898912      { 
    899         dataEnd--; 
     913        slpLength--; 
    900914      } 
    901915 
    902916      // Display the SLP body 
    903       QString slpMessage = QString::fromUtf8( message.data() + p2pDataStart, qMin( dataSize, (quint32) qMin( dataEnd, realEnd ) - p2pDataStart ) ); 
    904       logMessage += "\n<br>" + formatString(slpMessage); 
     917      QString slpMessage = QString::fromUtf8( dataPtr, slpLength ); 
     918      logMessage += "\n<br>" + formatString( slpMessage ); 
    905919 
    906920      // Add message footer 
    907921      if( hasSlpNullEnd ) 
    908922      { 
    909         // Display the \0 that QString::fromUtf8() silently dropped 
    910         logMessage += formatRawData( incoming, message, dataEnd, 1 ); 
     923        // Display the \0 that was dropped in the QString::fromUtf8() call. 
     924        logMessage += formatRawData( incoming, message, slpLength, 1 ); 
    911925      } 
    912926    } 
     927    else if( dataSize >= 18 && (quint8) message[ p2pDataStart ] == 0x80 ) 
     928    { 
     929      // Webcam invitation 
     930      QString camMessage = QString::fromUtf16( reinterpret_cast<const ushort *>( dataPtr + 10 ), ( safeSize - 10 - 1 ) / 2 ); 
     931 
     932      logMessage += "\n<br>" + formatRawData( incoming, message, p2pDataStart, 10, 1 ) // 1 per col. 
     933                  + "\n<br>" + formatString( camMessage ) 
     934                  + "\n<br>" + formatRawData( incoming, message, safeEnd - 1, 1 );  // final null. 
     935    } 
    913936    else 
    914937    { 
    915938      // Don't display the data. 
     
    918941 
    919942    // Display the remaining bytes, if any. 
    920943    // This is the footer when the p2p message is sent over the SB. 
    921     int remainingBytes = ( message.size() - dataEnd ); 
     944    int remainingBytes = ( message.size() - ( p2pDataStart + dataSize ) ); 
    922945    if( remainingBytes > 0 ) 
    923946    { 
    924       logMessage += "<br>" + formatRawData( incoming, message, dataEnd, remainingBytes ); 
     947      logMessage += "<br>" + formatRawData( incoming, message, safeEnd, remainingBytes ); 
    925948    } 
    926949 
    927950    return logMessage; 
     
    930953 
    931954 
    932955  // Format a utf-8 string to be displayed 
    933   QString NetworkWindow::formatRawData(bool incoming, const QByteArray &message, const int start, const int length, const int bytesPerRow) 
     956  QString NetworkWindow::formatRawData(bool incoming, const QByteArray &message, const int start, const int length, const int bytesPerCol, const int bytesPerRow) 
    934957  { 
    935958    QString logMessage; 
    936959    logMessage += "<tt><font color="; 
     
    949972        { 
    950973          logMessage += "<br>"; 
    951974        } 
    952         else if( i % 4 == 0 ) 
     975        else if( i % bytesPerCol == 0 ) 
    953976        { 
    954977          logMessage += " "; 
    955978        } 
     
    13251348 
    13261349 
    13271350#include "networkwindow.moc" 
    1328  
    1329 #endif // KMESS_NETWORK_WINDOW 
    1330  
  • dialogs/networkwindow.h

     
    1818 
    1919#include "../kmessdebug.h" 
    2020 
    21 #ifdef KMESS_NETWORK_WINDOW 
    22  
    2321#ifndef NETWORKWINDOW_H 
    2422#define NETWORKWINDOW_H 
    2523 
     
    9492    // Return a log description for the mime message 
    9593    QString                describeMimeMessage(const QString &mimeMessage); 
    9694    // Return a log description for the p2p message 
    97     QString                describeP2PMessage(const QByteArray &message, const int headerStart); 
     95    QString                describeP2PMessage(const QByteArray &message, const int headerStart, int footerCode); 
    9896    // Format the description to be displayed 
    9997    QString                formatDescription(const QString &description); 
    10098    // Format the message to be displayed 
    10199    QString                formatMessage(ConnectionEntry *entry, bool incoming, const QByteArray &message); 
    102100    // Format a msnp2p message to be displayed. 
    103     QString                formatP2PMessage(bool incoming, const QByteArray &message, const int headerStart); 
     101    QString                formatP2PMessage(bool incoming, const QByteArray &message, const int headerStart, int footerCode); 
    104102    // Format a utf-8 string to be displayed 
    105103    QString                formatRawData(bool incoming, const QByteArray &message, 
    106                                          const int start, const int length, const int bytesPerRow = 32); 
     104                                         const int start, const int length, const int bytesPerCol = 4, const int bytesPerRow = 32); 
    107105    // Format a utf-8 string to be displayed 
    108106    QString                formatString(const QString &message); 
    109107    // Return the current time. 
     
    140138 
    141139}; 
    142140 
    143  
    144  
    145141#endif 
    146  
    147 #endif // #ifdef KMESS_NETWORK_WINDOW