/*************************************************************************** msnconnection.cpp - description ------------------- begin : Thu Jan 23 2003 copyright : (C) 2003 by Mike K. Bennett email : mkb137b@hotmail.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "msnconnection.h" #include "mimemessage.h" #include "msnsocketbase.h" #include "msnsockettcp.h" #include "msnsockethttp.h" #include "multipacketmessage.h" #include "soap/httpsoapconnection.h" #include <KLocale> #include <KMessageBox> #ifdef KMESSDEBUG_CONNECTION // Area-specific debug statements #define KMESSDEBUG_CONNECTION_GENERAL #define KMESSDEBUG_CONNECTION_SOCKET #endif // Static members initialization bool MsnConnection::useHttpSocket_( false ); /** * @brief The constructor * * Initializes the sockets, buffers and ping timer. * * @param serverType The type of server connection: TCP or HTTP. */ 00047 MsnConnection::MsnConnection( MsnSocketBase::ServerType serverType ) : QObject(0) , ack_(0) , initialized_(false) { // Create the socket needed for this connection: default to the TCP one if( useHttpSocket_ ) { socket_ = new MsnSocketHttp( serverType ); } else { socket_ = new MsnSocketTcp( serverType ); } #ifdef KMESSTEST KMESS_ASSERT( socket_ != 0 ); #endif } /** * @brief The destructor * * Closes the connection, and deletes the sockets. */ 00074 MsnConnection::~MsnConnection() { // Disconnect from the server on exit if(isConnected()) { disconnectFromServer(); } // Clean up collections qDeleteAll( multiPacketBuffer_ ); multiPacketBuffer_.clear(); // Off course, delete the SOAP clients now here too. deleteSoapClients(); // Delete the socket delete socket_; #ifdef KMESSDEBUG_CONNECTION kDebug() << objectName() << "- DESTROYED"; #endif } /** * @brief Internal function to track SOAP clients. * * This method connects the HttpSoapConnection::soapError() signal, * and appends the client to the internal list. * * @param client The SOAP client class. */ 00107 void MsnConnection::addSoapClient( HttpSoapConnection *client ) { #ifdef KMESSDEBUG_NOTIFICATION_GENERAL kDebug() << "Adding SOAP client to list."; #endif // Connect signals connect( client, SIGNAL( soapError(QString,MsnSocketBase::ErrorType) ), this, SLOT ( slotError(QString,MsnSocketBase::ErrorType) ) ); // Add to list so it cleans up when the connection closes soapClients_.append( client ); } /** * @brief Connect a socket's signals to our slots * * Connects the signals of a new socket to this class' slots. * This is needed to have one place only for socket connection; * since socket connections are made in initialize() and in * switchToHttpSocket(). */ 00131 void MsnConnection::attachToSocketSignals() { #ifdef KMESSDEBUG_CONNECTION kDebug() << objectName() << "- Connecting socket signals."; #endif #ifdef KMESSTEST KMESS_ASSERT( socket_ != 0 ); #endif // Connect our integrative methods to the raw socket's events signals connect( socket_, SIGNAL( connected() ), this, SLOT ( slotConnected() ) ); connect( socket_, SIGNAL( disconnected() ), this, SLOT ( slotDisconnected() ) ); connect( socket_, SIGNAL( dataReceived(const QStringList&,const QByteArray&) ), this, SLOT ( slotDataReceived(const QStringList&,const QByteArray&) ) ); connect( socket_, SIGNAL( error(QString,MsnSocketBase::ErrorType) ), this, SLOT ( slotError(QString,MsnSocketBase::ErrorType) ) ); // Forward the socket's notification signals connect( socket_, SIGNAL( statusMessage(QString,bool) ), this, SIGNAL( statusMessage(QString,bool) ) ); connect( socket_, SIGNAL( pingSent() ), this, SIGNAL( pingSent() ) ); } /** * @brief Connect to the given server via the socket * * The connection attempt is asynchronous. * When the connection is made, connectionSuccess() is called, * on error connectionFailed() is called. * Connection timeouts are not detected here yet. * * @param server The server hostname or IP address. * @param port The port to connect to. */ 00169 void MsnConnection::connectToServer( const QString& server, const quint16 port ) { #ifdef KMESSDEBUG_CONNECTION_SOCKET if( server.isEmpty() && port == 0 ) { kDebug() << objectName() << "- Connecting to the default server."; } else { kDebug() << objectName() << "- Connecting to server at" << server << ":" << port << "."; } #endif // Start connecting now socket_->connectToServer( server, port ); } /** * @brief Disconnect and delete a SOAP clients. * * @param client The client to delete. * @see deleteSoapClients() */ 00194 void MsnConnection::deleteSoapClient( HttpSoapConnection *client ) { soapClients_.removeAll( client ); QTimer::singleShot( 10, client, SLOT(deleteLater()) ); // timeout needed for deleting passport login service. } /** * @brief Disconnect and delete all SOAP clients. * * This method is called by the derived class when all SOAP clients need to be deleted. * This is not done automatically in disconnectFromServer() so the derived class * is both responsable for creating and deleting the SOAP client objects. */ 00209 void MsnConnection::deleteSoapClients() { // Disconnect other possibly active SOAP sessions. // Use deleteLater() since this method may be called from loginIncorrect() if( ! soapClients_.isEmpty() ) { #ifdef KMESSDEBUG_NOTIFICATION_GENERAL kDebug() << "Closing SOAP sessions."; #endif foreach( HttpSoapConnection *soapClient, soapClients_ ) { soapClient->abort(); soapClient->deleteLater(); } soapClients_.clear(); } } /** * @brief Disconnect from the server * * This function is called after closeConnection() * It empties all buffers, and resets the sockets. * * @param isTransfer When set, a disconnected() signal won't be fired. * This is used to handle transfers to another server * without noticing a disconnect. */ 00241 void MsnConnection::disconnectFromServer( bool isTransfer ) { #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << objectName() << "- Closing the connection."; #endif // Gracefully disconnect if( isConnected() ) { sendCommand( "OUT" ); } // Stop pinging setSendPings( false ); // Clean up collections qDeleteAll( multiPacketBuffer_ ); multiPacketBuffer_.clear(); socket_->disconnectFromServer( isTransfer ); } /** * @brief Return the local IP address of the socket. * * This is typically a LAN address, unless the system * is directly connected to the Internet. * * @returns The IP address the socket uses at the system. */ 00273 const QString MsnConnection::getLocalIp() const { #ifdef KMESSDEBUG_SWITCHBOARD_FILETRANSFER kDebug() << objectName() << "- Local IP is " << socket_->getLocalIp(); #endif return socket_->getLocalIp(); } /** * @brief Initialize the object * * This method should be called before the class is used. * * It initializes the socket * Subclasses may re-implement this method to connect * to signals which are not available at startup in the constructor. */ 00293 bool MsnConnection::initialize() { if( initialized_ ) { kDebug() << objectName() << "- Already initialized!"; return false; } // Attach the socket's signals to this class attachToSocketSignals(); initialized_ = true; return true; } /** * @brief Return whether or the socket is connected. * @returns True when connected, false otherwise. */ 00314 bool MsnConnection::isConnected() const { return socket_->isConnected(); } /** * @brief Return whether the given command is an error command. * * This method just passes the question to the socket class * * @returns True when it's an error command, false otherwise. */ 00328 bool MsnConnection::isErrorCommand( const QString &command ) const { return socket_->isErrorCommand( command ); } /** * @brief Return whether the given command is a payload command. * * This method just passes the question to the socket class * * @returns True when it's a payload command, false otherwise. */ 00342 bool MsnConnection::isPayloadCommand( const QString &command ) const { return socket_->isPayloadCommand( command ); } // Process a received error command 00350 void MsnConnection::parseError( const QStringList &command, const QByteArray &payloadData ) { if( command[0].toInt() == 0 ) { kWarning() << "Invalid error command received:" << command; return; } // Always print error codes, to ease a bit the error resolution kWarning() << "Received error code" << command[0] << "from server."; if( ! payloadData.isEmpty() ) { kWarning() << "The error also carries a payload:"; kWarning() << payloadData; } QString errorMessage; bool isNotice = false; // Not really errors, will be displayed on the status bar bool isWarning = false; // Will be displayed on a dialog box, but will not disconnect MsnSocketBase::ErrorType errorType = MsnSocketBase::ERROR_USER; // By default, blame the user :) switch( command[0].toInt() ) { // Client side errors. // These errors are usually generated by misbehavior on kmess' or the user's side. case 200: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Invalid command syntax"); break; case 201: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Invalid command parameter"); break; case 205: errorMessage = i18n("The email address you have tried to add is not a Live Messenger account"); isWarning = true; break; case 206: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Domain name missing"); break; case 207: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Already logged in"); break; case 208: errorMessage = i18n("The given account name is invalid"); isWarning = true; break; case 209: errorMessage = i18n("This account name is invalid, or your Passport account has not been confirmed yet"); break; case 210: errorMessage = i18n("Your contact list is full"); isWarning = true; break; case 213: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Invalid SBP parameter"); break; case 215: errorMessage = i18n("This contact is already on your list"); isWarning = true; break; case 216: errorMessage = i18n("This contact is not on your list"); isWarning = true; break; case 217: errorMessage = i18n("This contact is not online"); isWarning = true; break; case 218: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Already in this mode"); break; case 219: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18nc("MSN error", "This contact cannot be added to both allow and block list"); break; case 222: case 228: errorMessage = i18n("The group name is already present in your contact list. Please use a different name"); isWarning = true; break; case 223: errorMessage = i18n("Your contact list has too many groups; you are allowed to only have at most 30"); isWarning = true; break; case 224: errorMessage = i18n("This group cannot be changed"); isWarning = true; break; case 225: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Contact is not added to this group"); isWarning = true; break; case 227: errorMessage = i18n("This group is not empty"); isWarning = true; break; case 229: errorMessage = i18n("The group name is too long; it cannot be longer than 61 characters"); isWarning = true; break; case 230: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("There was an internal error in KMess: %1", i18n("Attempted to change group \"0\"") ); break; case 231: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Invalid Group"); break; case 240: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Empty domain"); break; case 241: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Wrong ADL command format"); break; case 280: errorMessage = i18n("Switchboard server failed"); break; case 281: errorMessage = i18n("Transfer to switchboard server failed"); break; case 282: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Direct connection (MSNSLP) error, connection failed"); break; case 300: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Required field missing"); break; case 302: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Not logged in"); break; case 420: errorMessage = i18nc( "MSN error, due to e.g. trying the beta service without an allowed account", "This account was denied access to the Live Messenger service" ); break; // For other 5xx errors, see below case 502: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Command is disabled"); break; case 511: errorMessage = i18n("Your account is banned"); break; case 540: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Challenge response failed"); break; // For other 6xx errors, see below case 710: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Bad command data"); // bad CVR or URL command break; case 713: // Too many CAL's errorMessage = i18n("You are opening chat sessions too fast"); isWarning = true; break; case 714: errorMessage = i18n("You have too many open chat sessions"); isWarning = true; break; case 715: // Sent in response to a PRP setting an invalid phone type of three or less characters. // Also sent in response to a change of display name (PRP) on an unverified Passport account. errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Unexpected command sequence"); break; case 717: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Bad friend name"); isWarning = true; break; case 731: // Sent in response to a badly formatted CVR errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Bad CVR data"); break; case 800: // Happens when renaming too quickly, // but also when sending too many MSNP2P packets over the switchboard without waiting for ACKs. errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("The server reports KMess is flooding it with too much data"); isNotice = true; break; // For other 9xx errors, see below case 911: // User/pass was probably correct, TWN ticket was incorrect errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Authentication ticket was incorrect"); break; case 913: errorMessage = i18n("You cannot start a chat with someone while you are invisible"); isWarning = true; break; case 920: errorMessage = i18n("Not accepting new contacts"); isWarning = true; break; case 923: errorMessage = i18n("You have a Kids Passport account, you need parental consent to chat online"); break; case 924: errorMessage = i18n("Your Passport account needs to be verified first"); break; case 928: errorType = MsnSocketBase::ERROR_INTERNAL; errorMessage = i18n("Bad USR ticket"); break; // Server side errors. // These are generated without any intervention from us. case 402: case 403: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("Error accessing contact list, try again later"); break; case 500: // Generic server error case 501: // Database server error case 510: // File operation failed case 520: // Memory allocation failed case 603: // Database connection failed, in response to SYN case 605: // server unavailable errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("The Live Messenger service is temporarily unavailable. There was an internal server error"); break; case 600: case 601: // server is unavailable, in response to USR case 910: case 912: case 918: case 919: case 921: case 922: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("The Live Messenger service is temporarily unavailable. The server is too busy"); break; case 602: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("Peer notification server down"); break; case 604: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("The server is going down"); break; case 640: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("The server is going down soon"); isWarning = true; break; case 711: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18nc("Error received from the MSN servers", "Unknown error"); break; case 712: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("Session is overloaded"); isWarning = true; break; case 914: case 915: case 916: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("The server is not available"); break; case 917: errorType = MsnSocketBase::ERROR_SERVER; errorMessage = i18n("Authentication failed"); break; default: // No need to bugger the users with strange error messages kWarning() << "Unknown error" << command[0] << "received from the server! Full string:" << command.join( " " ); #ifdef KMESSDEBUG_NOTIFICATION_GENERAL slotError( i18n( "Unknown error code received from the server: %1<br/>Technical details: %2", command[0], payloadData ), MsnSocketBase::ERROR_INTERNAL ); #endif return; } // fontknocker: i'm opting to remove this. we only have one notice error, err 800, and there's nothing we can // do about it anyway. there's no point showing the user something that will make them nervous. // i'll display a debug message instead. if( isNotice ) { // emit statusMessage( errorMessage, false ); kDebug() << errorMessage; return; } // We've received a warning message: show it in a dialog box, but do not disconnect else if( isWarning ) { KMessageBox::error( 0, errorMessage, i18nc( "Error dialog box title with error number", "MSN Error %1", command[0].toInt() ) ); return; } // On debug builds, also display the payload if there is one along with the error. #ifdef KMESSDEBUG_NOTIFICATION_GENERAL errorMessage += "<br/><i>" + Qt::escape( payloadData ) + "</i>"; #endif // Call the unified error message display method slotError( errorMessage, errorType ); } /** * Internal processing of multi-packet messages. * @param command The received command. * @param mimeMessage The received mime message. */ 00678 void MsnConnection::parseMultiPacketMimeMessage( const QStringList &command, const MimeMessage &mimeMessage ) { #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << objectName() << "- Message is a multi-packet message, buffering."; #endif // Get associated multi-packet message buffer, create one if it doesn't exist yet. const QString& messageId( mimeMessage.getValue("Message-ID") ); MultiPacketMessage *multiPacketMessage; if( ! multiPacketBuffer_.contains( messageId ) ) { multiPacketMessage = new MultiPacketMessage; multiPacketBuffer_.insert( messageId, multiPacketMessage ); } else { multiPacketMessage = multiPacketBuffer_[ messageId ]; } // Add the message to the handler. multiPacketMessage->addChunk( mimeMessage ); if( ! multiPacketMessage->isComplete() ) { return; } // Parse message if complete multi-packet message is received. const MimeMessage &fullMimeMessage( multiPacketMessage->getMessage() ); parseMimeMessage( command, fullMimeMessage ); // Delete handler class delete multiPacketBuffer_.take( messageId ); } /** * @brief Send a command to the server * * This function automatically inserts the ACK-number * between the prefix and text. * This can be used later to track error responses. * * @param prefix The three-letter command, which parseCommand() receives in <code>command[0]</code>. * @param text The command arguments, joined with spaces. * @returns The ack-number used by the command. */ 00725 int MsnConnection::sendCommand( const QString& prefix, const QString &text ) { int ack = ack_++; // Send the data to the server const QString command( prefix + " " + QString::number( ack ) + " " + text + "\r\n" ); writeData( command ); // Return the ack used return ack; } /** * @brief Send a MIME message command to the server. * @returns, returning the ack */ 00743 int MsnConnection::sendMimeMessage( AckType ackType, const MimeMessage &message ) { // Determine which type to send. // Normally a char[4] lookup array would be sufficient, // but this is easier to use in the string concatenation with Qt const char *ackTypeStr = "?"; switch(ackType) { case ACK_NONE: ackTypeStr = "U"; break; case ACK_NAK_ONLY: ackTypeStr = "N"; break; case ACK_ALWAYS: ackTypeStr = "A"; break; case ACK_ALWAYS_P2P: ackTypeStr = "D"; break; } // Create the "MSG id type length" header const QByteArray& rawMessage( message.getMessage() ); // Build the command int ack = ack_++; QString command; command.sprintf("MSG %u %s %u\r\n", ack, ackTypeStr, rawMessage.size()); // Write the data writeBinaryData( command.toUtf8() + rawMessage ); // Return the ack used return ack; } // Send a payload command to the server, convenience function int MsnConnection::sendPayloadMessage( const QString &prefix, const QString &arguments, const QString &payload ) { // Send payload, as UTF-8 encoded raw string. const QByteArray& binPayload( payload.toUtf8() ); return sendPayloadMessage( prefix, arguments, binPayload ); } // Send a payload command to the server int MsnConnection::sendPayloadMessage( const QString &prefix, const QString &arguments, const QByteArray &payload ) { int ack = ack_++; // Build the command QString command( prefix + " " + QString::number( ack ) ); if( ! arguments.isEmpty() ) { command += " " + arguments; } command += " " + QString::number( payload.size() ) + "\r\n"; // Write the data if( payload.size() > 0 ) { writeBinaryData( command.toUtf8() + payload ); } else { writeData( command ); } // Return the ack used return ack; } // Specify which accepted commands carry payload data for this connection void MsnConnection::setAcceptedPayloadCommands( QStringList commandList ) { socket_->setAcceptedPayloadCommands( commandList ); } // Set whether we're sending pings or not void MsnConnection::setSendPings( bool sendPings ) { socket_->setSendPings( sendPings ); } /** * @brief Called when data is received from the server. * * This method parses the incoming data for the syntax used by the MSN protocol. * * A parsed message to handled to one of the following methods: * - one-line commands are delivered to parseCommand() * - <code>MSG</code> commands with MIME payloads are delivered to parseMimeMessage() * - other commands with payloads are delivered to parsePayloadMessage(). * This uses isPayloadCommand() to detect if a command has a payload. */ 00842 void MsnConnection::slotDataReceived( const QStringList &commandLine, const QByteArray &payloadData ) { #ifdef KMESSDEBUG_SERVERMESSAGES // Display the received command data kDebug() << objectName() << "- <<< (C) " << commandLine; if( ! payloadData.isEmpty() ) { kDebug() << objectName() << "- <<< (P) " << payloadData; } #endif #ifdef KMESS_NETWORK_WINDOW KMESS_NET_RECEIVED( this, commandLine.join(" ").toUtf8() ); // Also display the payload if present if( ! payloadData.isEmpty() ) { KMESS_NET_RECEIVED( this, payloadData ); } #endif // See if it's a normal or payload command, or an error if( isErrorCommand( commandLine[0] ) ) { parseError( commandLine, payloadData ); } else if( isPayloadCommand( commandLine[0] ) ) { // See if it's a Mime message. if( commandLine[0] != "MSG" ) { // Other payload message types are parsed in the subclasses. parsePayloadMessage( commandLine, payloadData ); } else { // It's a mime message // Note we can't parse strings here, the message could contain binary p2p data. // UTF-8 characters are translated in the MimeMessage constructor itself. MimeMessage mimeMessage( payloadData ); // Detect Multi-packet messages if( ! mimeMessage.hasField("Message-ID") ) { // It's a normal message, parse it. parseMimeMessage( commandLine, mimeMessage ); } else { // Handle multi-packet message parts. parseMultiPacketMimeMessage( commandLine, mimeMessage ); } } } else { // It's a normal command, handle it in the subclass. parseCommand( commandLine ); } } /** * @brief Called when the socket is disconnected. */ 00907 void MsnConnection::slotDisconnected() { #ifdef KMESS_NETWORK_WINDOW KMESS_NET_CLOSE(this); #endif emit disconnected(); } /** * @brief If it is possible, switch to the HTTP connection * * This method checks what connection is being established, and if is a TCP * connection, will delete it and re-start connecting using the HTTP method. * * This is used when the TCP connection process fails. */ 00926 bool MsnConnection::switchToHttpSocket() { // Check if we're already using HTTP if( qobject_cast<MsnSocketHttp*>( socket_ ) ) { #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << "Cannot switch to HTTP connection. Class is" << socket_->metaObject()->className(); #endif return false; } #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << "Attempting switch to HTTP connection."; #endif // Save the socket details to use the same ones for the new socket MsnSocketBase::ServerType serverType = socket_->getServerType(); QStringList acceptedPayloadCommands = socket_->getAcceptedPayloadCommands(); // Close and disable the old server socket_->blockSignals( true ); socket_->disconnectFromServer(); socket_->deleteLater(); // Replace it with the new one socket_ = new MsnSocketHttp( serverType ); socket_->setAcceptedPayloadCommands( acceptedPayloadCommands ); // Set the internal socket switch useHttpSocket_ = true; // Attach its signals to this class attachToSocketSignals(); // Start connecting connectToServer(); emit statusMessage( i18n("Trying the HTTP fallback..."), false ); return true; } /** * @brief Switch back to the TCP connection * * This method checks what connection is being established, and if is an HTTP * connection, will delete it and start using again the TCP method. * * This is used after disconnecting from an HTTP session, to try again with TCP the next time. */ 00977 bool MsnConnection::switchToTcpSocket() { // Check if we're already using TCP if( qobject_cast<MsnSocketTcp*>( socket_ ) ) { #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << "Cannot switch to TCP connection. Class is" << socket_->metaObject()->className(); #endif return false; } #ifdef KMESSDEBUG_CONNECTION_SOCKET kDebug() << "Attempting to switch back to TCP connection."; #endif // Save the socket details to use the same ones for the new socket MsnSocketBase::ServerType serverType = socket_->getServerType(); QStringList acceptedPayloadCommands = socket_->getAcceptedPayloadCommands(); // Close and disable the old server socket_->blockSignals( true ); disconnect( socket_, 0 ); socket_->disconnectFromServer(); socket_->deleteLater(); // Replace it with the new one socket_ = new MsnSocketTcp( serverType ); socket_->setAcceptedPayloadCommands( acceptedPayloadCommands ); // Set the internal socket switch useHttpSocket_ = false; // Attach its signals to this class attachToSocketSignals(); return true; } // Write data to the socket without conversions void MsnConnection::writeBinaryData( const QByteArray& data ) { if( ! isConnected() ) { kWarning() << objectName() << "- Attempting to write binary data while disconnected, aborting."; return; } #ifdef KMESSDEBUG_SERVERMESSAGES // To display the entire contents: /* QByteArray debugData( data ); for( int i = 0; i < debugData.size(); i++ ) { if( debugData[i] < 10 ) debugData[ i ] = '?'; } kDebug() << objectName() << "- >>> " << QString::fromUtf8( debugData.data(), debugData.size() ); */ // To display the first readable part: (removing the final \r\n) kDebug() << objectName() << "- >>> " << QString( data ).trimmed(); #endif // Write to the socket socket_->writeBinaryData( data ); // Append the message to the network window #ifdef KMESS_NETWORK_WINDOW KMESS_NET_SENT( this, data ); #endif } // Write data to the socket void MsnConnection::writeData( const QString& data ) { if( ! isConnected() ) { kWarning() << objectName() << "- Attempting to write data while disconnected, aborting."; return; } #ifdef KMESSDEBUG_SERVERMESSAGES kDebug() << objectName() << "- >>> " << data.trimmed(); #endif const QByteArray& binData( data.toUtf8() ); qint64 noBytesWritten = socket_->writeData( binData ); if( noBytesWritten != binData.length() ) // Note that the previous code used data.length instead of unicode.length(), but I { // changed this since that is the string we send to the socket... (could be wrong here) kWarning() << objectName() << "- Wanted to write " << data.length() << " bytes to the socket, wrote " << noBytesWritten << "."; } // Append message to the network window #ifdef KMESS_NETWORK_WINDOW KMESS_NET_SENT( this, data.toUtf8() ); #endif } #include "msnconnection.moc"