Ticket #193: webcam-firststeps.patch
| File webcam-firststeps.patch, 56.4 KB (added by diederik, 4 years ago) |
|---|
-
network/applications/filetransferp2p.cpp
473 473 << ( usePreview ? "with thumbnail." : "without thumbnail." ); 474 474 #endif 475 475 476 // Create the short name477 uint shortNameLength = fileName_.length();478 479 476 // Get the file data (preview 480 477 ulong filesize = fileData->size(); 481 478 int flags = (hasPreview ? 0 : 1); … … 493 490 P2PMessage::insertBytes( context, flags, 16 ); // Field 4: 1 if NO preview data 494 491 495 492 // 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 ); 504 494 P2PMessage::insertBytes(context, 0xFFFFFFFF, 570); // Field 6: some splitter field. 505 495 506 496 // Insert preview data … … 836 826 } 837 827 838 828 829 839 830 /** 840 831 * @brief Called when the thumbnail is generated. 841 832 */ … … 843 834 { 844 835 bool hasPreview = thumbnailProvider_->isSuccessful(); 845 836 846 // Create the session id and context field847 QString context = createContextField( file_, hasPreview );848 uint sessionID = (uint)KMessShared::generateID();849 850 837 // Send the invitation 851 sendSlpSessionInvitation(sessionID, getAppId(), 2, context); 838 QString context = createContextField( file_, hasPreview ); 839 sendSlpSessionInvitation( KMessShared::generateID(), getAppId(), 2, context ); 852 840 853 854 841 // Generate the HTML to cancel the transfer 855 842 QString html = i18n( "Sending file "%1" (%2).", 856 843 "<span class=\"filename invitationFilename\">" + fileName_ + "</span>", -
network/applications/p2papplicationbase.cpp
286 286 } 287 287 288 288 // 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. 290 290 // - the UniqueID is not set. (also happens with normal ack messages in GAIM 1.5). 291 291 // 292 292 // This happens with 0x01 control messages: … … 863 863 shouldSendAck_ = true; 864 864 gotDataPreparation(p2pMessage); 865 865 } 866 // Check for data messages867 else if( p2pMessage.isMsnObjectData()868 || p2pMessage.isFileData())866 // Check for normal data messages 867 else if( p2pMessage.isMsnObjectData() 868 || p2pMessage.isFileData()) 869 869 { 870 870 gotDataFragment(p2pMessage); 871 871 } 872 else if( waitingState_ == P2P_WAIT_FOR_FILE_DATA873 && p2pMessage.getFlags() == 0874 && p2pMessage.isFragment())872 else if( waitingState_ == P2P_WAIT_FOR_FILE_DATA 873 && p2pMessage.getFlags() == 0 874 && p2pMessage.isFragment()) 875 875 { 876 876 // HACK: added for Kopete 0.9.2 code (has no flag set with data messages). 877 877 kWarning() << "Expecting data message, " … … 882 882 " class=" << metaObject()->className() << ")!"; 883 883 gotDataFragment(p2pMessage); 884 884 } 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 } 885 895 else 886 896 { 887 897 // Unknown p2p message … … 1187 1197 // Reserve the next message ID. 1188 1198 nextMessageID_++; 1189 1199 1190 // Set the total size field, this causes sendP2PMessage ()1200 // Set the total size field, this causes sendP2PMessageImpl() 1191 1201 // to handle fragmented messages correctly. 1192 1202 // The 'fragmentMessageID_' is set so KMess sends all fragments with the same message ID. 1193 1203 // It could happen the client receives a SLP ACK meanwhile, and ACKs it with a new message ID. … … 1220 1230 1221 1231 // TODO: Check the various footer codes send for certain transfers. 1222 1232 QByteArray p2pMessage( 4, 0x00 ); 1223 sendP2PMessage (p2pMessage, 0, 1, P2P_MSG_DATA_PREPARATION);1233 sendP2PMessageImpl( p2pMessage, 0, P2P_TYPE_PICTURE, P2P_MSG_DATA_PREPARATION ); 1224 1234 1225 1235 // TODO: after the data preparation is sent, WLM8 starts a direct connection setup: 1226 1236 // - it does not send an ACK yet … … 1373 1383 << " buffer=" << bytesRead; 1374 1384 #endif 1375 1385 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. 1377 1387 unsigned long nextProgressValue = ( fragmentOffset_ + bytesRead ); 1378 1388 1379 1389 // Determine the flags: 1380 1390 // Funny, MSN6 doesn't seam to care which flag was set 1381 1391 // while transferring the file. KMess however, does. 1382 bool writeSuccess = false;1392 uint flag = 0; 1383 1393 switch( dataType_ ) 1384 1394 { 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; 1393 1397 default: 1394 1398 // Display error message for the first message only. 1395 1399 if( fragmentOffset_ == 0 ) … … 1401 1405 " class=" << metaObject()->className() << 1402 1406 " action=tryflag)."; 1403 1407 } 1404 1405 writeSuccess = sendP2PMessage( data, 0, 1, P2P_MSG_DATA, fragmentMessageID_ ); // The best solution in this situation1406 1408 } 1407 1409 1410 // Send the message 1411 bool writeSuccess = sendP2PMessageImpl( data, flag, dataType_, P2P_MSG_DATA, fragmentMessageID_ ); 1412 1408 1413 // Avoid next round if write was blocked already. 1409 1414 // This could happen if a direct connection can't write all data. 1410 1415 // The DirectConnectionBase class will automatically send the remaining parts, so don't worry about that here. … … 1524 1529 1525 1530 1526 1531 /** 1527 * @brief Send a P2P ACK message.1532 * @brief Internal function to send a P2P ACK message. 1528 1533 * 1529 1534 * This method is the internal implementation for sendP2PAck() and others. 1530 1535 * … … 1693 1698 1694 1699 1695 1700 /** 1696 * @brief Sends a P2P Message.1701 * @brief Sends a complete P2P message payload. 1697 1702 * 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 */ 1713 void 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 * 1698 1749 * This method constructs the P2P binary header fields. 1699 1750 * The constructed message is delivered to ApplicationList::sendMessage(). 1700 1751 * … … 1705 1756 * - outgoingMessages_ is updated to trace ACKs back later. 1706 1757 * 1707 1758 * The fragmentTotalSize_ should be set to the total size before sending message fragments. 1708 * This is done automatically by send SlpMessage().1759 * This is done automatically by sendP2PMessage() function, which is part of the public API. 1709 1760 * 1710 1761 * @param messageData The message payload to send. This can be binary file data or a SLP MIME data. 1711 1762 * @param flagField The value for the flag field in the P2P header. 1712 1763 * @param footerCode The message footer, which is appended when the message is sent over the switchboard. 1713 1764 * @param messageType The type of the message. This value is stored with in the unacked message queue. 1714 1765 * 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. 1716 1769 * @return Whether the message can be sent. If the socket would block, false is returned. 1717 1770 * This only occurs for direct connections connections. 1718 1771 */ 1719 bool P2PApplicationBase::sendP2PMessage (const QByteArray &messageData, int flagField, uintfooterCode, P2PMessageType messageType, unsigned long messageID)1772 bool P2PApplicationBase::sendP2PMessageImpl(const QByteArray &messageData, int flagField, P2PDataType footerCode, P2PMessageType messageType, unsigned long messageID) 1720 1773 { 1721 1774 #ifdef KMESSTEST 1722 1775 KMESS_ASSERT( messageData.size() <= 1202 ); … … 1762 1815 1763 1816 1764 1817 // Determine the message size: 1818 // Internal trick: fragmentTotalSize_ is set by the caller if this is a splitted message. 1765 1819 uint messageSize = messageData.size(); 1766 1820 ulong totalSize; 1767 1821 ulong offsetField; … … 1786 1840 // Set session ID to zero when SLP messages are sent. 1787 1841 unsigned long sessionID = getSessionID(); 1788 1842 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'. 1790 1845 { 1791 1846 sessionID = 0; 1792 1847 } … … 1804 1859 1805 1860 1806 1861 // 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 ); 1808 1863 if( ! writeSuccess ) 1809 1864 { 1810 1865 // This only happens when a direct connection transfer is congested. … … 1896 1951 */ 1897 1952 void P2PApplicationBase::sendP2PWaitingError() 1898 1953 { 1899 sendP2PMessage (0, P2PMessage::MSN_FLAG_WAITING, 0);1954 sendP2PMessageImpl( 0, P2PMessage::MSN_FLAG_WAITING ); 1900 1955 shouldSendAck_ = false; 1901 1956 } 1902 1957 … … 1905 1960 /** 1906 1961 * @brief Send a complete SLP message in multiple P2P packets. 1907 1962 * 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 *1912 1963 * This is a low-level function which is primary used by 1913 1964 * P2PApplication::sendSlpInvitation(), P2PApplication::sendSlpBye(), etc.. 1914 1965 * 1966 * It converts the SLP string to the payload data, 1967 * which could be splitted across multiple P2P messages. 1968 * 1915 1969 * @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(). 1917 1971 */ 1918 1972 void P2PApplicationBase::sendSlpMessage(const QString &slpMessage, P2PMessageType messageType) 1919 1973 { … … 1928 1982 // Make sure the extra padded '\0' character is there. MSN 6-WLM rely on this. 1929 1983 utf8Message.append('\0'); 1930 1984 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. 1958 1986 } 1959 1987 1960 1988 … … 2137 2165 } 2138 2166 2139 2167 // 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!! 2141 2169 */ 2142 2170 testUnAckedMessages( true ); // TODO: argument is not used, and replaces sendP2PWaitingError(). 2143 2171 -
network/applications/webapplicationp2p.cpp
82 82 // int realAppID = appID.mid(4, 4).toInt(); 83 83 84 84 // 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' 86 86 QByteArray decodedContext = QByteArray::fromBase64( context.toLatin1() ); 87 QString contextString = QString::fromUtf16( reinterpret_cast<const ushort*>( decodedContext.data() ), decodedContext.size() / 2 ); 87 88 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 91 89 // Get the fields 92 90 QString unknown = contextString.section(';', 1, 1); 93 91 QString appName = contextString.section(';', 2, 2); -
network/applications/p2papplication.cpp
2787 2787 2788 2788 #ifdef KMESSTEST 2789 2789 // Test, as other invocations likely result in undefined client behavour 2790 // (2791 2790 KMESS_ASSERT( gotSlpMessage_ ); 2792 KMESS_ASSERT( isWaitingState( P2P_WAIT_DEFAULT ) );2791 KMESS_ASSERT( isWaitingState( P2P_WAIT_DEFAULT ) || isWaitingState( P2P_WAIT_USER_ACCEPT ) ); 2793 2792 KMESS_ASSERT( ! callID_.isEmpty() ); 2794 2793 KMESS_ASSERT( ! branch_.isEmpty() ); 2795 2794 #endif -
network/applications/p2papplicationbase.h
228 228 P2P_MSG_DATA = 5, ///< The actual data message. 229 229 P2P_MSG_SESSION_BYE = 6, ///< The SLP BYE message. 230 230 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. 232 233 }; 233 234 234 235 /** … … 242 243 , P2P_TYPE_PICTURE = 1 ///< Packet contains MsnObject data. 243 244 , P2P_TYPE_FILE = 2 ///< Packet contains file data. 244 245 , P2P_TYPE_INK = 3 ///< Packet contains an Ink message. 246 , P2P_TYPE_WEBCAM = 4 ///< Packet contains a webcam setup message. 245 247 }; 246 248 247 249 /** … … 260 262 P2P_WAIT_DEFAULT = 0, ///< Not waiting at all. 261 263 P2P_WAIT_FOR_SLP_OK_ACK = 1, ///< Waiting for remote client to ack the the SLP OK message. 262 264 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. 263 266 P2P_WAIT_FOR_PREPARE = 3, ///< Waiting for remote client to send some prepare message. 264 267 P2P_WAIT_FOR_PREPARE_ACK = 4, ///< Waiting for remote client to ack the data preparation. 265 268 P2P_WAIT_FOR_SLP_BYE = 5, ///< Waiting for remote client to send the SLP BYE. … … 332 335 void sendP2PAbort(); 333 336 // Send an low-level ACK message for a received message if this is needed. 334 337 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); 335 340 // Send a low-level error message that the application is waiting for a certain message. 336 341 void sendP2PWaitingError(); 337 342 // Send a given string using sendP2PMessage() … … 384 389 // Send a P2P ACK message 385 390 void sendP2PAckImpl(int ackType = P2PMessage::MSN_FLAG_ACK, UnAckedMessage *originalMessageData = 0); 386 391 // 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); 388 393 // Test if there are still unacked messages. 389 394 void testUnAckedMessages(bool sendError); 390 395 -
network/applications/webcamtransferp2p.cpp
18 18 #include "webcamtransferp2p.h" 19 19 20 20 #include "../../kmessdebug.h" 21 #include "../../utils/kmessshared.h" 21 22 #include "../mimemessage.h" 22 23 23 24 #include <KLocale> 25 #include <stdlib.h> // rand() 24 26 25 27 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 // 26 33 34 35 36 27 37 /** 28 38 * Constructor 29 39 * … … 31 41 */ 32 42 WebcamTransferP2P::WebcamTransferP2P(ApplicationList *applicationList) 33 43 : P2PApplication(applicationList) 44 , isUserSender_(false) 34 45 { 35 46 setApplicationType( ChatMessage::TYPE_APPLICATION_WEBCAM ); 36 47 } … … 63 74 * 64 75 * @param message The invitation message 65 76 */ 66 void WebcamTransferP2P::contactStarted1_ContactInvitesUser(const MimeMessage & /*message*/)77 void WebcamTransferP2P::contactStarted1_ContactInvitesUser(const MimeMessage & message) 67 78 { 68 #ifdef KMESSDEBUG_WEB APPLICATION_P2P79 #ifdef KMESSDEBUG_WEBCAMTRANSFER_P2P 69 80 kDebug(); 70 81 #endif 71 82 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"); 76 87 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 << "."; 81 91 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; 84 109 #endif 85 110 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 } 91 113 92 // Tell the contact we don't support this.93 sendCancelMessage(CANCEL_NOT_INSTALLED);94 114 95 // // Everything seams OK, accept this message: 96 // contactStarted2_UserAccepts(); 115 116 /** 117 * Step two of a contact-started chat: the user accepts 118 */ 119 void 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 97 132 } 98 133 99 134 100 135 136 101 137 /** 138 * Step three of a contact-started chat: the contact confirms the accept 139 */ 140 void 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 */ 154 QString 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 */ 211 QString WebcamTransferP2P::getConferenceAppId() 212 { 213 return "{4BD96FC0-AB17-4425-A14A-439185962DC8}"; 214 } 215 216 217 218 /** 102 219 * Return the application's GUID for "user push" invitations. 103 220 */ 104 221 QString WebcamTransferP2P::getPushAppId() … … 117 234 } 118 235 119 236 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 */ 244 void 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 */ 299 void 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 */ 320 void 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 */ 348 void 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 */ 358 void WebcamTransferP2P::userStarted3_UserPrepares() 359 { 360 361 362 } 363 364 365 120 366 #include "webcamtransferp2p.moc" -
network/applications/webcamtransferp2p.h
44 44 // The destructor 45 45 virtual ~WebcamTransferP2P(); 46 46 47 // Return the application's GUID for "video conference" invitations. 48 static QString getConferenceAppId(); 49 47 50 // Return the application's GUID (for the "user push" invitation) 48 51 static QString getPushAppId(); 49 52 … … 54 57 55 58 // Step one of a contact-started chat: the contact invites the user 56 59 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_; 57 85 }; 58 86 59 87 #endif -
network/p2pmessage.cpp
103 103 return ackDataSize_; 104 104 } 105 105 106 106 107 // Retreives the nonce field (for direct connection handshake. 107 108 QString P2PMessage::getNonce() const 108 109 { … … 258 259 ackSessionID_ = extractBytes ( messageData, 32); 259 260 ackUniqueID_ = extractBytes ( messageData, 36); 260 261 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 } 261 272 } 262 273 263 274 … … 267 278 // ------------------------------------------------------ 268 279 // A utility to fill the byte data block 269 280 281 282 270 283 void P2PMessage::insertBytes(QByteArray &buffer, const unsigned int value, const int offset) 271 284 { 272 285 // Also based on Kopete code. I started with a nice struct, … … 294 307 // However, note the bytes are placed in network order. (big endian) 295 308 } 296 309 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 }302 310 311 303 312 void P2PMessage::insertNonce(QByteArray &buffer, const QString &nonce, const int offset) 304 313 { 305 314 // Remove the separators … … 327 336 } 328 337 } 329 338 339 340 341 // Copy sort integers into the binary header. 342 void 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 330 357 unsigned int P2PMessage::extractBytes(const char *data, const int offset) 331 358 { 332 359 // Convert the bytes from network order to a normal int. … … 336 363 | ((unsigned char) data[offset + 3] << 24)); 337 364 } 338 365 366 367 339 368 unsigned long P2PMessage::extractLongBytes(const char *data, const int offset) 340 369 { 341 370 // Convert the bytes from network order to a long int. … … 354 383 } 355 384 356 385 386 357 387 QString P2PMessage::extractNonce(const char *data, const int offset) 358 388 { 359 389 const int noncePos[] = { 3,2,1,0 // Field 1 reversed … … 380 410 return "{" + hex + "}"; 381 411 } 382 412 413 414 415 QString 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
104 104 105 105 // Copy integers into the binary header. 106 106 static void insertBytes(QByteArray& buffer, const unsigned int value, const int offset); 107 // Copy s ort integers into the binary header.107 // Copy short integers into the binary header. 108 108 static void insertShortBytes(QByteArray &buffer, const unsigned short value, const int offset); 109 109 // Copy a nonce string into the binary header. 110 110 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); 111 113 // Extracts the bytes from the data block. 112 114 static unsigned int extractBytes(const char *data, const int offset); 113 115 // Extracts the bytes from the data block. 114 116 static unsigned long extractLongBytes(const char *data, const int offset); 115 117 // Extracts the nonce string from the data header. 116 118 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); 117 121 118 122 119 123 private: // Helper functions -
network/msnnotificationconnection.cpp
495 495 #else 496 496 // NOTE: When changing this property, all MSNP2P code need to be retested again!! 497 497 // 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 ); 499 499 #endif 500 500 501 501 QString statusCode = MsnStatus::getCode( newStatus ); -
chat/chatmaster.h
125 125 void slotSwitchboardReady(); 126 126 // Start a netmeeting invitation 127 127 void startNetMeeting(const QString &handle); 128 // Start a webcam transfer with the contact 129 void startWebcamTransfer( const QString &handle ); 128 130 129 131 private: // private methods 130 132 // Create and register a new switchboard -
chat/chatwindow.cpp
493 493 494 494 // Create the actions 495 495 sendAction_ = new KAction( KIcon("folder-remote"), i18n("Send a &File"), this ); 496 webcamAction_ = new KAction( KIcon("webcamsend"), i18n("Webcam chat"), this ); 496 497 meetingAction_ = new KAction( KIcon("gnomemeeting"), i18n("Start a &Meeting"), this ); 497 498 nudgeAction_ = new KAction( KIcon("preferences-desktop-notification-bell"), i18n("Send a &Nudge!"), this ); 498 499 saveAction = new KAction( KIcon("document-save"), i18n("Save chat"), this ); 499 500 addEmoticonAction_ = new KAction( KIcon("emoticons"), i18n("&Emoticons"), this ); 501 closeAllAction_ = new KAction( KIcon("dialog-close"), i18n("Close &All Tabs"), this ); 500 502 closeAction = KStandardAction::close( this, SLOT( queryClose() ), actionCollection_ ); 501 closeAllAction_ = new KAction( KIcon("dialog-close"), i18n("Close &All Tabs"), this );502 503 503 504 // Initially the chat has one tab only, so the Close All option is not useful 504 505 closeAllAction_->setVisible( false ); … … 512 513 inviteButton_->setDelayed( false ); 513 514 514 515 // 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() ) ); 527 523 528 524 #ifdef HAS_KPHONE // If KPhone is not enabled, don't support voice conversation 529 525 KAction *conversation = new KAction( "kphone", i18n("Start or Stop a &Conversation"), this ); … … 534 530 // The Invite menu goes before this separator 535 531 firstChatMenuItem_ = chatMenu_->addSeparator(); 536 532 chatMenu_->addAction( sendAction_ ); 537 chatMenu_->addAction( meetingAction_ ); 533 chatMenu_->addAction( webcamAction_ ); 534 chatMenu_->addAction( meetingAction_ ); // TODO: remove this. 538 535 #ifdef HAS_KPHONE // If KPhone is not enabled, don't support voice conversation 539 536 chatMenu_->addAction( conversation ); 540 537 #endif … … 548 545 // removed 'close', not a frequently used action (and you'll have Alt+F4 and the window close button for that). 549 546 toolBar()->addAction( inviteButton_ ); 550 547 toolBar()->addAction( sendAction_ ); 548 toolBar()->addAction( webcamAction_ ); 551 549 toolBar()->addAction( nudgeAction_ ); 552 550 toolBar()->addAction( addEmoticonAction_ ); 553 551 … … 569 567 570 568 // Create the "edit" menus 571 569 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() ) ); 577 573 578 574 // Add shorter texts for the toolbar 579 575 changeFont ->setIconText( i18n("&Font") ); … … 1467 1463 1468 1464 1469 1465 1466 // Start a file transfer 1467 void ChatWindow::startWebcamTransfer() 1468 { 1469 getCurrentChat()->startWebcamTransfer(); 1470 } 1471 1472 1470 1473 // Start a voice conversation 1471 1474 void ChatWindow::startConversation() 1472 1475 { -
chat/chatwindow.h
180 180 void startFileTransfer(); 181 181 // Start GnomeMeeting with a contact. 182 182 void startMeeting(); 183 // Start a webcam transfer 184 void startWebcamTransfer(); 183 185 // Called when the "use emoticons" action is called. 184 186 void toggleEmoticons( bool useEmoticons ); 185 187 // Called when the "show sidebar" action is called. … … 188 190 void toggleSpellCheck( bool useSpellCheck ); 189 191 190 192 private: // private attributes 193 // ActionCollection 194 KActionCollection *actionCollection_; 191 195 // The add emoticon action 192 196 KAction *addEmoticonAction_; 193 197 // A timer used to make the caption blink on new messages. … … 250 254 QTimer statusTimer_; 251 255 // The bar of chat tabs 252 256 KTabBar *tabBar_; 253 // Action Collection254 KAction Collection *actionCollection_;257 // Action to start a file transfer 258 KAction *webcamAction_; 255 259 256 260 signals: // Public signals 257 261 // Signal that the window is about to close. -
chat/chat.cpp
1261 1261 1262 1262 1263 1263 1264 // Send webcam data to a contact. 1265 void 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 1264 1282 #include "chat.moc" -
chat/chat.h
106 106 void startConversation(); 107 107 // Start GnomeMeeting with a contact. 108 108 void startMeeting(); 109 // Send webcam data to a contact. 110 void startWebcamTransfer(); 109 111 110 112 private: // Private methods 111 113 // Check if the user enabled an auto-reply, and send it … … 166 168 void requestFileTransfer(const QString &handle, const QString &filename); 167 169 // Signal that the ChatMaster should start a newmeeting session. 168 170 void requestNetMeeting(const QString &handle); 171 // Signal that the ChatMaster should start a webcam transfer. 172 void requestWebcamTransfer(const QString &handle); 169 173 // The user wants to add or remove a contact. 170 174 void setContactAdded( QString handle, bool isAdded ); 171 175 // The user wants to allow a contact to see his/hers online status. -
chat/chatmaster.cpp
23 23 #include "../contact/msnobject.h" 24 24 #include "../network/applications/applicationlist.h" 25 25 #include "../network/applications/mimeapplication.h" 26 #include "../network/applications/msnobjecttransferp2p.h"27 26 #include "../network/applications/p2papplication.h" 28 27 #include "../network/applications/filetransfer.h" 29 28 #include "../network/applications/filetransferp2p.h" 30 29 #include "../network/applications/gnomemeeting.h" 31 30 #include "../network/applications/inktransferp2p.h" 31 #include "../network/applications/msnobjecttransferp2p.h" 32 #include "../network/applications/webcamtransferp2p.h" 32 33 #include "../network/chatmessage.h" 33 34 #include "../network/msnswitchboardconnection.h" 34 35 #include "../currentaccount.h" … … 1473 1474 this, SLOT ( startFileTransfer(const QString&,const QString&) ) ); 1474 1475 connect( newChat, SIGNAL( requestNetMeeting(const QString&) ), 1475 1476 this, SLOT ( startNetMeeting(const QString&) ) ); 1477 connect( newChat, SIGNAL( requestWebcamTransfer(const QString&) ), 1478 this, SLOT ( startWebcamTransfer(const QString&) ) ); 1476 1479 connect( newChat, SIGNAL( closing(QObject*) ), 1477 1480 this, SLOT ( slotChatClosing(QObject*) ) ); 1478 1481 connect( newChat, SIGNAL( destroyed(QObject*) ), … … 1651 1654 1652 1655 if( contact != 0 && contact->hasP2PSupport() ) 1653 1656 { 1654 // The contact supports file transfer over MSNP2P1655 P2PApplication *app = new FileTransferP2P(appList, filename);1656 startApplication(app);1657 }1658 else1659 {1660 1657 // This should be so rare that it justifies a warning message. 1661 1658 if( contact == 0 ) 1662 1659 { … … 1670 1667 // The contact only supports file transfer the old way 1671 1668 MimeApplication *app = new FileTransfer(handle, filename); 1672 1669 startApplication(app); 1670 return; 1673 1671 } 1672 1673 // The contact supports file transfer over MSNP2P 1674 P2PApplication *app = new FileTransferP2P(appList, filename); 1675 startApplication(app); 1674 1676 } 1675 1677 1676 1678 … … 1761 1763 1762 1764 1763 1765 1766 // Start a webcam transfer with the contact 1767 void 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 1764 1787 /** 1765 1788 * Periodically called method for update commands. 1766 1789 * -
kmessdebug.h
87 87 88 88 #define KMESSDEBUG_MIMEMESSAGE 89 89 #define KMESSDEBUG_MULTIPACKETMESSAGE 90 #define KMESSDEBUG_P2PMESSAGE 90 91 #define KMESSDEBUG_APPLICATION 91 92 #define KMESSDEBUG_APPLICATIONLIST 92 93 #define KMESSDEBUG_MIMEAPPLICATION … … 96 97 #define KMESSDEBUG_FILETRANSFER 97 98 #define KMESSDEBUG_REMOTEDESKTOP 98 99 #define KMESSDEBUG_WEBAPPLICATION_P2P 100 #define KMESSDEBUG_WEBCAMTRANSFER_P2P 99 101 100 102 #define KMESSDEBUG_UPNP 101 103 #define KMESSDEBUG_SOAPMESSAGE -
dialogs/networkwindow.cpp
17 17 18 18 #include "../kmessdebug.h" 19 19 20 #ifdef KMESS_NETWORK_WINDOW21 22 20 #include "networkwindow.h" 23 21 24 22 #include "../network/mimemessage.h" … … 457 455 458 456 459 457 // 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) 461 459 { 460 Q_UNUSED(footerCode); 461 462 462 // Prepare to extract certain P2P header fields 463 463 QDataStream binaryStream( message ); 464 464 binaryStream.setByteOrder( QDataStream::LittleEndian ); … … 504 504 } 505 505 } 506 506 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 507 516 // No flags means the contents is a string with SLP-like mime fields, used to negotiate the session. 508 517 // datasize of 0 is not handed here, is an invalid packet. 509 518 // KMess does parse it as ACK message for compatibility with broken clients. … … 711 720 else 712 721 { 713 722 // 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); 716 725 } 717 726 break; 718 727 … … 759 768 // Extract the first utf-8 mime part, locate start of p2p header 760 769 QString p2pMimePart = utf8Message.left(mimeEnd + 4); 761 770 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. 762 772 763 773 // Add the mime part and binary p2p part 764 774 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); 767 777 } 768 778 } 769 779 else … … 821 831 822 832 823 833 // 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) 825 835 { 836 Q_UNUSED(footerCode); 837 826 838 QString logMessage; 827 839 828 840 // Prepare to extract certain P2P header fields … … 862 874 } 863 875 864 876 // Show header information 865 logMessage += formatRawData(incoming, message, headerStart, 48, 16);877 logMessage += formatRawData(incoming, message, headerStart, 48, 4, 16); 866 878 867 879 // Add header debug details 868 880 logMessage += "<font color='#333333'>"; … … 887 899 logMessage += "</font>"; 888 900 889 901 // The message a utf-8 (MSNSLP) payload if the session id is zero. 902 const char *dataPtr = ( message.data() + p2pDataStart ); 890 903 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 ) ); 893 906 if( hasSlpPayload ) 894 907 { 895 908 // 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 ); 897 911 if( hasSlpNullEnd ) 898 912 { 899 dataEnd--;913 slpLength--; 900 914 } 901 915 902 916 // 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 ); 905 919 906 920 // Add message footer 907 921 if( hasSlpNullEnd ) 908 922 { 909 // Display the \0 that QString::fromUtf8() silently dropped910 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 ); 911 925 } 912 926 } 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 } 913 936 else 914 937 { 915 938 // Don't display the data. … … 918 941 919 942 // Display the remaining bytes, if any. 920 943 // 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 ) ); 922 945 if( remainingBytes > 0 ) 923 946 { 924 logMessage += "<br>" + formatRawData( incoming, message, dataEnd, remainingBytes );947 logMessage += "<br>" + formatRawData( incoming, message, safeEnd, remainingBytes ); 925 948 } 926 949 927 950 return logMessage; … … 930 953 931 954 932 955 // 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 bytesPer Row)956 QString NetworkWindow::formatRawData(bool incoming, const QByteArray &message, const int start, const int length, const int bytesPerCol, const int bytesPerRow) 934 957 { 935 958 QString logMessage; 936 959 logMessage += "<tt><font color="; … … 949 972 { 950 973 logMessage += "<br>"; 951 974 } 952 else if( i % 4== 0 )975 else if( i % bytesPerCol == 0 ) 953 976 { 954 977 logMessage += " "; 955 978 } … … 1325 1348 1326 1349 1327 1350 #include "networkwindow.moc" 1328 1329 #endif // KMESS_NETWORK_WINDOW1330 -
dialogs/networkwindow.h
18 18 19 19 #include "../kmessdebug.h" 20 20 21 #ifdef KMESS_NETWORK_WINDOW22 23 21 #ifndef NETWORKWINDOW_H 24 22 #define NETWORKWINDOW_H 25 23 … … 94 92 // Return a log description for the mime message 95 93 QString describeMimeMessage(const QString &mimeMessage); 96 94 // 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); 98 96 // Format the description to be displayed 99 97 QString formatDescription(const QString &description); 100 98 // Format the message to be displayed 101 99 QString formatMessage(ConnectionEntry *entry, bool incoming, const QByteArray &message); 102 100 // 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); 104 102 // Format a utf-8 string to be displayed 105 103 QString formatRawData(bool incoming, const QByteArray &message, 106 const int start, const int length, const int bytesPer Row = 32);104 const int start, const int length, const int bytesPerCol = 4, const int bytesPerRow = 32); 107 105 // Format a utf-8 string to be displayed 108 106 QString formatString(const QString &message); 109 107 // Return the current time. … … 140 138 141 139 }; 142 140 143 144 145 141 #endif 146 147 #endif // #ifdef KMESS_NETWORK_WINDOW
