diff options
author | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
---|---|---|
committer | toma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2009-11-25 17:56:58 +0000 |
commit | bcb704366cb5e333a626c18c308c7e0448a8e69f (patch) | |
tree | f0d6ab7d78ecdd9207cf46536376b44b91a1ca71 /kopete/protocols/msn/msnsocket.cpp | |
download | tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.tar.gz tdenetwork-bcb704366cb5e333a626c18c308c7e0448a8e69f.zip |
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdenetwork@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kopete/protocols/msn/msnsocket.cpp')
-rw-r--r-- | kopete/protocols/msn/msnsocket.cpp | 1099 |
1 files changed, 1099 insertions, 0 deletions
diff --git a/kopete/protocols/msn/msnsocket.cpp b/kopete/protocols/msn/msnsocket.cpp new file mode 100644 index 00000000..a650cd83 --- /dev/null +++ b/kopete/protocols/msn/msnsocket.cpp @@ -0,0 +1,1099 @@ +/* + msnsocket.cpp - Base class for the sockets used in MSN + + Copyright (c) 2002-2003 by Martijn Klingens <klingens@kde.org> + Copyright (c) 2002-2005 by Olivier Goffart <ogoffart at kde.org> + Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com> + + Kopete (c) 2002-2005 by the Kopete developers <kopete-devel@kde.org> + + Portions of this code are taken from KMerlin, + (c) 2001 by Olaf Lueg <olueg@olsd.de> + + ************************************************************************* + * * + * 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 "msnsocket.h" +//#include "msnprotocol.h" + +#include <qregexp.h> +#include <qtimer.h> + +#include <kdebug.h> +#include <kconfig.h> +#include <kbufferedsocket.h> +#include <kserversocket.h> +#include <kresolver.h> +#include <klocale.h> +#include <kmessagebox.h> +#include <kurl.h> + +#include "kopeteuiglobal.h" + +using namespace KNetwork; + +class MimeMessage +{ + public: + MimeMessage(const QString &msg) : message(msg) {} + + QString getValue(const QString &key) + { + QRegExp rx(key+": ([^\r\n]+)"); + rx.search(message); + return rx.cap(1); + } + private: + QString message; +}; + +MSNSocket::MSNSocket(QObject* parent) : QObject (parent) +{ + m_onlineStatus = Disconnected; + m_socket = 0L; + m_useHttp = false; + m_timer = 0L; +} + +MSNSocket::~MSNSocket() +{ + //if ( m_onlineStatus != Disconnected ) + // disconnect(); + delete m_timer; + m_timer = 0L; + doneDisconnect(); + if ( m_socket ) + m_socket->deleteLater(); +} + +void MSNSocket::connect( const QString &server, uint port ) +{ + if ( m_onlineStatus == Connected || m_onlineStatus == Connecting ) + { + kdWarning( 14140 ) << k_funcinfo << "Already connected or connecting! Not connecting again." << endl; + return; + } + + if( m_onlineStatus == Disconnecting ) + { + // Cleanup first. + // FIXME: More generic!!! + kdWarning( 14140 ) << k_funcinfo << "We're still disconnecting! Deleting socket the hard way first." << endl; + delete m_socket; + } + + setOnlineStatus( Connecting ); + m_id = 0; + //m_lastId = 0; + m_waitBlockSize = 0; + m_buffer = Buffer( 0 ); + + //m_sendQueue.clear(); + + m_server = server; + m_port = port; + + if(!m_useHttp) + m_socket = new KBufferedSocket( server, QString::number(port) ); + else { + m_socket = new KBufferedSocket( m_gateway, "80" ); + } + + m_socket->enableRead( true ); + + // enableWrite eats the CPU, and we only need it when the queue is + // non-empty, so disable it until we have actual data in the queue + m_socket->enableWrite( false ); + + QObject::connect( m_socket, SIGNAL( readyRead() ), this, SLOT( slotDataReceived() ) ); + QObject::connect( m_socket, SIGNAL( readyWrite() ), this, SLOT( slotReadyWrite() ) ); + QObject::connect( m_socket, SIGNAL( hostFound() ), this, SLOT( slotHostFound() ) ); + QObject::connect( m_socket, SIGNAL( connected( const KResolverEntry&) ), this, SLOT( slotConnectionSuccess() ) ); + QObject::connect( m_socket, SIGNAL( gotError( int ) ), this, SLOT( slotSocketError( int ) ) ); + QObject::connect( m_socket, SIGNAL( closed( ) ), this, SLOT( slotSocketClosed( ) ) ); + + if(m_useHttp) + { + if(m_timer == 0L) + { + m_timer = new QTimer(this, "Http poll timer"); + // Connect the slot HttpPoll with the timer timeout signal. + QObject::connect(m_timer, SIGNAL(timeout()), this, SLOT(slotHttpPoll())); + } + } + + aboutToConnect(); + + // start the asynchronous connection + m_socket->connect(); +} + +void MSNSocket::disconnect() +{ + if(m_useHttp) + if(m_timer->isActive()) { + // If the timer is still active, stop the timer. + m_timer->stop(); + } + + if ( m_socket ) + m_socket->closeNow(); + else + slotSocketClosed(); +} + +void MSNSocket::aboutToConnect() +{ + /* Empty default implementation */ +} + +void MSNSocket::doneConnect() +{ + setOnlineStatus( Connected ); +} + +void MSNSocket::doneDisconnect() +{ + setOnlineStatus( Disconnected ); +} + +void MSNSocket::setOnlineStatus( MSNSocket::OnlineStatus status ) +{ + if ( m_onlineStatus == status ) + return; + + m_onlineStatus = status; + emit onlineStatusChanged( status ); +} + +void MSNSocket::slotSocketError( int error ) +{ + kdWarning( 14140 ) << k_funcinfo << "Error: " << error << " (" << m_socket->errorString() << ")" << endl; + + if(!KSocketBase::isFatalError(error)) + return; + //we only care about fatal error + + QString errormsg = i18n( "There was an error while connecting to the MSN server.\nError message:\n" ); + if ( error == KSocketBase::LookupFailure ) + errormsg += i18n( "Unable to lookup %1" ).arg( m_socket->peerResolver().nodeName() ); + else + errormsg += m_socket->errorString() ; + + //delete m_socket; + m_socket->deleteLater(); + m_socket = 0L; + + setOnlineStatus( Disconnected ); + emit connectionFailed(); + //like if the socket is closed + emit socketClosed(); + + emit errorMessage( ErrorConnectionError, errormsg ); +} + +void MSNSocket::slotDataReceived() +{ + int avail = m_socket->bytesAvailable(); + if ( avail < 0 ) + { + // error! + kdWarning( 14140 ) << k_funcinfo << "bytesAvailable() returned " << avail + << ". This should not happen!" << endl + << "Are we disconnected? Backtrace:" << endl << kdBacktrace() << endl; + return; + } + + // incoming data, plus an extra char where we pretend a NUL is so the conversion + // to QCString doesn't go over the end of the allocated memory. + char *buffer = new char[ avail + 1 ]; + int ret = m_socket->readBlock( buffer, avail ); + + if ( ret < 0 ) + { + kdWarning( 14140 ) << k_funcinfo << "readBlock() returned " << ret << "!" <<endl; + } + else if ( ret == 0 ) + { + kdWarning( 14140 ) << k_funcinfo << "readBlock() returned no data!" <<endl; + } + else + { + if ( avail ) + { + if ( ret != avail) + { + kdWarning( 14140 ) << k_funcinfo << avail << " bytes were reported available, " + << "but readBlock() returned only " << ret << " bytes! Proceeding anyway." << endl; + } + } + else + { + kdDebug( 14140 ) << k_funcinfo << "Read " << ret << " bytes into 4kb block." << endl; + } + + + QString rawData; + + if(m_useHttp) + { + bool error = false; + QByteArray bytes; + + // Check if all data has arrived. + rawData = QString(QCString(buffer, avail + 1)); + bool headers = (rawData.find(QRegExp("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)")) != -1); + + if(headers) + { + // The http header packet arrived. + int endOfHeaders = rawData.find("\r\n\r\n"); + if((endOfHeaders + 4) == avail) + { + // Only the response headers data is included. + QRegExp re("Content-Length: ([^\r\n]+)"); + if(re.search(rawData) != -1) + { + bool valid; + int l = re.cap(1).toInt(&valid); + if(valid && l > 0) + { + // The packet contains the headers but does not contain the content data; + // buffer the data received and read again. + m_buffer.add(buffer, avail); + + delete[] buffer; + // Update how much data remains. + m_remaining = l; + return; + } + } + } + } + else + { + // Write the received data to the buffer. + m_buffer.add(buffer, avail); + + m_remaining -= avail; + if(m_remaining != 0) + { + // We have not received all the content data, read again. + delete[] buffer; + return; + } + + // At this point, we have all the bytes returned from the web request. + bytes = m_buffer.take(m_buffer.size()); + } + + if(bytes.size() == 0) + { + // The response headers and the content came in one packet. + bytes.assign(buffer, avail); + } + + + // Create the web response object from the response bytes. + WebResponse response(bytes); + + if(response.getStatusCode() == 100) { + return; + } + + if(response.getStatusCode() == 200) + { + // If we received a valid response, read the required headers. + // Retrieve the X-MSN-Messenger header. + QString header = response.getHeaders()->getValue("X-MSN-Messenger"); + + QStringList parts = QStringList::split(";", header.replace(" ", "")); + if(!header.isNull() && (parts.count() >= 2)) + { + if(parts[0].find("SessionID", 0) != -1) + { + // Assign the session id. + m_sessionId = parts[0].section("=", 1, 1); + }else + error = true; + + if(parts[1].find("GW-IP", 0) != -1) + { + // Assign the gateway IP address. + m_gwip = parts[1].section("=", 1, 1); + }else + error = true; + + + if(parts.count() > 2) + if((parts[2].find("Session", 0) != -1) && (parts[2].section("=", 1, 1) == "close")) + { + // The http session has been closed by the server, disconnect. + kdDebug(14140) << k_funcinfo << "Session closed." << endl; + m_bCanPoll = false; + disconnect(); + return; + } + }else + error = true; + + // Retrieve the content length header. + header = response.getHeaders()->getValue("Content-Length"); + + if(!header.isNull()) + { + bool valid; + int length = header.toInt(&valid); + if(valid && (length == 0)) + { + // If the response content length is zero, there is nothing to do. + m_pending = false; + return; + } + + if(valid && (length > 0)) + { + // Otherwise, if the content length is greater than zero, get the web response stream. + QDataStream *stream = response.getResponseStream(); + buffer = new char[length]; + // Read the web response content. + stream->readRawBytes(buffer, length); + ret = length; + }else + error = true; + }else + error = true; + }else + error = true; + + if(error) + { + kdDebug(14140) << k_funcinfo << "Http error: " << response.getStatusCode() << " " + << response.getStatusDescription() << endl; + + // If we encountered an error, disconnect and return. + m_bCanPoll = false; + // Disconnect from the service. + disconnect(); + return; + } + } + + // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug: + // all MSN commands start with one or more uppercase characters. + // For now just check the first three chars, let's see how accurate it is. + // Additionally, if we receive an MSN-P2P packet, strip off anything after the P2P header. + rawData = QString( QCString( buffer, ((!m_useHttp)? avail : ret) + 1 ) ).stripWhiteSpace().replace( + QRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" ); + + bool isBinary = false; + for ( uint i = 0; i < 3 ; ++i ) + { + if ( (rawData[ i ] < 'A' || rawData[ i ] > 'Z') && (rawData[ i ] < '0' || rawData[ i ] > '9') ) + isBinary = true; + } + + if ( isBinary ) + kdDebug( 14141 ) << k_funcinfo << "(Stripped binary data)" << endl; + else + kdDebug( 14141 ) << k_funcinfo << rawData << endl; + + // fill the buffer with the received data + m_buffer.add( buffer, ret ); + + slotReadLine(); + + if(m_useHttp) { + // Set data pending to false. + m_pending = false; + } + } + + // Cleanup. + delete[] buffer; +} + +void MSNSocket::slotReadLine() +{ + // We have data, first check if it's meant for a block read, otherwise + // parse the first line (which will recursively parse the other lines) + if ( !pollReadBlock() ) + { + if ( m_buffer.size() >= 3 && ( m_buffer.data()[ 0 ] == '\0' || m_buffer.data()[ 0 ]== '\1' ) ) + { + bytesReceived( m_buffer.take( 3 ) ); + QTimer::singleShot( 0, this, SLOT( slotReadLine() ) ); + return; + } + + int index = -1; + for ( uint x = 0; m_buffer.size() > x + 1; ++x ) + { + if ( ( m_buffer[ x ] == '\r' ) && ( m_buffer[ x + 1 ] == '\n' ) ) + { + index = x; + break; + } + } + + if ( index != -1 ) + { + QString command = QString::fromUtf8( m_buffer.take( index + 2 ), index ); + command.replace( "\r\n", "" ); + //kdDebug( 14141 ) << k_funcinfo << command << endl; + + // Don't block the GUI while parsing data, only do a single line! + // (Done before parseLine() to prevent a potential crash) + QTimer::singleShot( 0, this, SLOT( slotReadLine() ) ); + + parseLine( command ); + // WARNING: At this point 'this' can be deleted (when disconnecting) + } + } +} + +void MSNSocket::readBlock( uint len ) +{ + if ( m_waitBlockSize ) + { + kdWarning( 14140 ) << k_funcinfo << "Cannot wait for data block: still waiting for other block of size " + << m_waitBlockSize << "! Data will not be returned." << endl; + return; + } + + m_waitBlockSize = len; + + //kdDebug( 14140 ) << k_funcinfo << "Preparing for block read of size " << len << endl; + + // Try to return the data now, if available. Otherwise slotDataReady + // will do this whenever all data is there. + pollReadBlock(); +} + +bool MSNSocket::pollReadBlock() +{ + if ( !m_waitBlockSize ) + { + return false; + } + else if ( m_buffer.size() < m_waitBlockSize ) + { + kdDebug( 14140 ) << k_funcinfo << "Waiting for data. Received: " << m_buffer.size() << ", required: " << m_waitBlockSize << endl; + return true; + } + + QByteArray block = m_buffer.take( m_waitBlockSize ); + + //kdDebug( 14140 ) << k_funcinfo << "Successfully read block of size " << m_waitBlockSize << endl; + + m_waitBlockSize = 0; + emit blockRead( block); + + return false; +} + +void MSNSocket::parseLine( const QString &str ) +{ + QString cmd = str.section( ' ', 0, 0 ); + QString data = str.section( ' ', 2 ).replace( "\r\n" , "" ); + + bool isNum; + uint id = str.section( ' ', 1, 1 ).toUInt( &isNum ); + + // In some rare cases, like the 'NLN' / 'FLN' commands no id at all + // is sent. Here it's actually a real parameter... + if ( !isNum ) + data = str.section( ' ', 1, 1 ) + " " + data; + + //if ( isNum && id ) + // m_lastId = id; + + //kdDebug( 14140 ) << k_funcinfo << "Parsing command " << cmd << " (ID " << id << "): '" << data << "'" << endl; + + data.replace( "\r\n", "" ); + bool isError; + uint errorCode = cmd.toUInt( &isError ); + if ( isError ) + handleError( errorCode, id ); + else + parseCommand( cmd, id, data ); +} + +void MSNSocket::handleError( uint code, uint /* id */ ) +{ + kdDebug(14140) << k_funcinfo << endl; + QString msg; + ErrorType type = ErrorServerError; + switch ( code ) + { +/* + // We cant show message for error we don't know what they are or not related to the correct socket + // Theses following messages are not so instructive + case 205: + msg = i18n ( "An invalid username has been specified.\nPlease correct it, and try to reconnect.\n" ); + break; + case 201: + msg = i18n ( "Fully Qualified domain name missing.\n" ); + break; + case 207: + msg = i18n ( "You are already logged in!\n" ); + break; + case 208: + msg = i18n ( "You specified an invalid username.\nPlease correct it, and try to reconnect.\n"); + break; + case 209: + msg = i18n ( "Your nickname is invalid. Please check it, correct it,\nand try to reconnect.\n" ); + break; + case 210: + msg = i18n ( "Your list has reached its maximum capacity.\nNo more contacts can be added, unless you remove some first.\n" ); + break; + case 216: + msg = i18n ( "This user is not in your contact list.\n " ); + break; + case 300: + msg = i18n ( "Some required fields are missing. Please fill them in and try again.\n" ); + break; + case 302: + msg = i18n ( "You are not logged in.\n" ); + break; +*/ + case 500: + msg = i18n ( "An internal server error occurred. Please try again later." ); + type = MSNSocket::ErrorCannotConnect; + break; + case 502: + msg = i18n ( "It is no longer possible to perform this operation. The MSN server does not allow it anymore." ); + type = MSNSocket::ErrorServerError; + break; + case 600: + case 910: + case 912: + case 921: + case 922: + msg = i18n ( "The MSN server is busy. Please try again later." ); + type = MSNSocket::ErrorConnectionError; + break; + case 601: + case 604: + case 605: + case 914: + case 915: + case 916: + case 917: + msg = i18n ( "The server is not available at the moment. Please try again later." ); + type = MSNSocket::ErrorCannotConnect; + break; + // Server error + default: + // FIXME: if the error causes a disconnect, it will crash, but we can't disconnect every time + msg = i18n( "Unhandled MSN error code %1 \n" + "Please fill a bug report with a detailed description and if possible the last console debug output." ).arg( code ); + // "See http://www.hypothetic.org/docs/msn/basics.php for a description of all error codes." + break; + } + + if ( !msg.isEmpty() ) + emit errorMessage( type, msg ); + + return; +} + +int MSNSocket::sendCommand( const QString &cmd, const QString &args, bool addId, const QByteArray &body, bool binary ) +{ + if ( !m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "m_socket == NULL!" << endl; + return -1; + } + + QCString data = cmd.utf8(); + if ( addId ) + data += " " + QString::number( m_id ).utf8(); + + if ( !args.isEmpty() ) + data += " " + args.utf8(); + + // Add length in bytes, not characters + if ( !body.isEmpty() ) + data += " " + QString::number( body.size() - (binary ? 0 : 1 ) ).utf8(); + + data += "\r\n"; + + + // the command will be sent in slotReadyWrite + QByteArray bytes; + const uint length = data.length(); + bytes.duplicate(data.data(), length); + if(!body.isEmpty()) + { + uint l = body.size() - (binary ? 0 : 1); + bytes.resize(length + l); + for(uint i=0; i < l; i++) + bytes[length + i] = body[i]; + } + + // Add the request to the queue. + m_sendQueue.append(bytes); + m_socket->enableWrite(true); + + if ( addId ) + { + ++m_id; + return m_id - 1; + } + + return 0; +} + +void MSNSocket::slotReadyWrite() +{ + if ( !m_sendQueue.isEmpty() ) + { + // If the command queue is not empty, retrieve the first command. + QValueList<QByteArray>::Iterator it = m_sendQueue.begin(); + + if(m_useHttp) + { + // If web response data is not pending, send the http request. + if(!m_pending) + { + m_pending = true; + // Temporarily disable http polling. + m_bCanPoll = false; + // Set the host to the msn gateway by default. + QString host = m_gateway; + QString query; // Web request query string. + + if(m_bIsFirstInTransaction) + { + query.append("Action=open&Server="); + query.append(m_type); + + query += "&IP=" + m_server; + + m_bIsFirstInTransaction = false; + } + else + { + // If this is not the first request sent in the transaction, + // only add the session Id. + host = m_gwip; + query += "SessionID=" + m_sessionId; + } + + // Create the web request headers. + QString s = makeHttpRequestString(host, query, (*it).size()); + + uint length = s.length(); + // Create the web request bytes. + QByteArray bytes(length + (*it).size()); + + // Copy the request headers into the request bytes. + for(uint i=0; i < length; i++) + bytes[i] = s.ascii()[i]; + // Copy the request body into the request bytes. + for(uint i=0; i < (*it).size(); i++) + bytes[length + i] = (*it)[i]; + + kdDebug( 14141 ) << k_funcinfo << "Sending http command: " << QString(*it).stripWhiteSpace() << endl; + + // Write the request bytes to the socket. + m_socket->writeBlock(bytes.data(), bytes.size()); + + // Remove the request from the request queue. + m_sendQueue.remove(it); + + if(m_sendQueue.isEmpty()) + { + // Disable sending requests. + m_socket->enableWrite(false); + // If the request queue is empty, poll the server. + m_bCanPoll = true; + } + } + } + else + { + // Otherwise, send the command normally. + + // Simple check to avoid dumping the binary data from the icons and emoticons to kdDebug: + // When sending an MSN-P2P packet, strip off anything after the P2P header. + QString debugData = QString( *it ).stripWhiteSpace().replace( + QRegExp( "(P2P-Dest:.[a-zA-Z@.]*).*" ), "\\1\n\n(Stripped binary data)" ); + kdDebug( 14141 ) << k_funcinfo << "Sending command: " << debugData << endl; + + m_socket->writeBlock( *it, ( *it ).size() ); + m_sendQueue.remove( it ); + + // If the queue is empty agalin stop waiting for readyWrite signals + // because of the CPU usage + if ( m_sendQueue.isEmpty() ) + m_socket->enableWrite( false ); + } + } + else + { + m_socket->enableWrite( false ); + + if(m_useHttp) + { + // If the request queue is empty, poll the server. + m_bCanPoll = true; + } + } +} + +QString MSNSocket::escape( const QString &str ) +{ + //return ( KURL::encode_string( str, 106 ) ); + //It's not needed to encode everything. The official msn client only encode spaces and % + //If we encode more, the size can be longer than excepted. + + int old_length= str.length(); + QChar *new_segment = new QChar[ old_length * 3 + 1 ]; + int new_length = 0; + + for ( int i = 0; i < old_length; i++ ) + { + unsigned short character = str[i].unicode(); + + if( character <= 32 || character == '%' ) + { + new_segment[ new_length++ ] = '%'; + + unsigned int c = character / 16; + c += (c > 9) ? ('A' - 10) : '0'; + new_segment[ new_length++ ] = c; + + c = character % 16; + c += (c > 9) ? ('A' - 10) : '0'; + new_segment[ new_length++ ] = c; + } + else + new_segment[ new_length++ ] = str[i]; + } + + QString result = QString(new_segment, new_length); + delete [] new_segment; + return result; + +} + +QString MSNSocket::unescape( const QString &str ) +{ + QString str2 = KURL::decode_string( str, 106 ); + //remove msn+ colors code + str2 = str2.replace( QRegExp("[\\x1-\\x8]"), "" ); // old msn+ colors + // added by kaoul <erwin.kwolek at gmail.com> + str2 = str2.replace( QRegExp("\\xB7[&@\'#0]"),""); // dot ... + str2 = str2.replace( QRegExp("\\xB7\\$,?\\d{1,2}"),""); // dot dollar (comma)? 0-99 + + return str2; +} + +void MSNSocket::slotConnectionSuccess() +{ + if(m_useHttp) + { + // If we are connected, set the data pending flag to false, + // and disable http polling. + m_pending = false; + m_bCanPoll = false; + // If we are connected, start the timer. + m_timer->start(2000, false); + } + + //kdDebug( 14140 ) << k_funcinfo << endl; + doneConnect(); +} + +void MSNSocket::slotHostFound() +{ + // nothing to do +} + +void MSNSocket::slotSocketClosed() +{ + kdDebug( 14140 ) << k_funcinfo << "Socket closed. " << endl; + + if ( !m_socket || m_onlineStatus == Disconnected ) + { + kdDebug( 14140 ) << k_funcinfo << "Socket already deleted or already disconnected" << endl; + return; + } + + doneDisconnect(); + + m_buffer = Buffer( 0 ); + //delete m_socket; + m_socket->deleteLater(); + m_socket = 0L; + + emit socketClosed(); +} + +void MSNSocket::slotHttpPoll() +{ + if(m_pending || !m_bCanPoll){ + // If data is pending or poll has been temporary disabled, return. + return; + } + + // Create the http request headers. + const QCString headers = makeHttpRequestString(m_gwip, "Action=poll&SessionID=" + m_sessionId, 0).utf8(); + m_socket->writeBlock(headers, headers.length()); + // Wait for the response. + m_pending = true; + m_socket->enableWrite(true); +} + +// Used in MSNFileTransferSocket +// FIXME: Why is this here if it's only used for file transfer? - Martijn +void MSNSocket::bytesReceived( const QByteArray & /* data */ ) +{ + kdWarning( 14140 ) << k_funcinfo << "Unknown bytes were received" << endl; +} + +void MSNSocket::sendBytes( const QByteArray &data ) +{ + if ( !m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "Not yet connected" << endl; + return; + } + + m_socket->writeBlock( data, data.size() ); + m_socket->enableWrite( true ); +} + +bool MSNSocket::setUseHttpMethod( bool useHttp ) +{ + if( m_useHttp == useHttp ) + return true; + + if( useHttp ) { + QString s = QString( this->className() ).lower(); + if( s == "msnnotifysocket" ) + m_type = "NS"; + else if( s == "msnswitchboardsocket" ) + m_type = "SB"; + else + m_type = QString::null; + + if( m_type.isNull() ) + return false; + + m_bCanPoll = false; + m_bIsFirstInTransaction = true; + m_pending = false; + m_remaining = 0; + m_gateway = "gateway.messenger.hotmail.com"; + } + + if ( m_onlineStatus != Disconnected ) + disconnect(); + + m_useHttp = useHttp; + + return true; +} + +bool MSNSocket::useHttpMethod() const +{ + return m_useHttp; +} + +bool MSNSocket::accept( KServerSocket *server ) +{ + if ( m_socket ) + { + kdWarning( 14140 ) << k_funcinfo << "Socket already exists!" << endl; + return false; + } + + m_socket = static_cast<KBufferedSocket*>(server->accept()); + + if ( !m_socket ) + { +// kdWarning( 14140 ) << k_funcinfo << "Socket not created. Error nb" << server->error() << " : " << server->errorString() << endl; + return false; + } + + kdDebug( 14140 ) << k_funcinfo << "incoming connection accepted" << endl; + + setOnlineStatus( Connecting ); + + m_id = 0; + //m_lastId = 0; + m_waitBlockSize = 0; + + m_socket->setBlocking( false ); + m_socket->enableRead( true ); + m_socket->enableWrite( true ); + + QObject::connect( m_socket, SIGNAL( readyRead() ), this, SLOT( slotDataReceived() ) ); + QObject::connect( m_socket, SIGNAL( readyWrite() ), this, SLOT( slotReadyWrite() ) ); + QObject::connect( m_socket, SIGNAL( closed() ), this, SLOT( slotSocketClosed() ) ); + QObject::connect( m_socket, SIGNAL( gotError( int ) ), this, SLOT( slotSocketError( int ) ) ); + + doneConnect(); + return true; +} + +QString MSNSocket::getLocalIP() +{ + if ( !m_socket ) + return QString::null; + + const KSocketAddress address = m_socket->localAddress(); + + QString ip = address.nodeName(); + + kdDebug( 14140 ) << k_funcinfo << "IP: " << ip <<endl; + //delete address; + return ip; +} + +MSNSocket::Buffer::Buffer( unsigned int sz ) +: QByteArray( sz ) +{ +} + +MSNSocket::Buffer::~Buffer() +{ +} + +void MSNSocket::Buffer::add( char *str, unsigned int sz ) +{ + char *b = new char[ size() + sz ]; + for ( uint f = 0; f < size(); f++ ) + b[ f ] = data()[ f ]; + for ( uint f = 0; f < sz; f++ ) + b[ size() + f ] = str[ f ]; + + duplicate( b, size() + sz ); + delete[] b; +} + +QByteArray MSNSocket::Buffer::take( unsigned blockSize ) +{ + if ( size() < blockSize ) + { + kdWarning( 14140 ) << k_funcinfo << "Buffer size " << size() << " < asked size " << blockSize << "!" << endl; + return QByteArray(); + } + + QByteArray rep( blockSize ); + for( uint i = 0; i < blockSize; i++ ) + rep[ i ] = data()[ i ]; + + char *str = new char[ size() - blockSize ]; + for ( uint i = 0; i < size() - blockSize; i++ ) + str[ i ] = data()[ blockSize + i ]; + duplicate( str, size() - blockSize ); + delete[] str; + + return rep; +} + +QString MSNSocket::makeHttpRequestString(const QString& host, const QString& query, uint contentLength) +{ + QString s( + "POST http://" + host + "/gateway/gateway.dll?" + query + " HTTP/1.1\r\n" + + "Accept: */*\r\n" + + "Accept-Language: en-us\r\n" + + "User-Agent: MSMSGS\r\n" + + "Host: " + host + "\r\n" + + "Proxy-Connection: Keep-Alive\r\n" + + "Connection: Keep-Alive\r\n" + + "Pragma: no-cache\r\n" + + "Content-Type: application/x-msn-messenger\r\n" + + "Content-Length: " + QString::number(contentLength) + "\r\n" + + "\r\n"); + return s; +} + +MSNSocket::WebResponse::WebResponse(const QByteArray& bytes) +{ + m_statusCode = 0; + m_stream = 0; + + int headerEnd; + QString header; + QString data(QCString(bytes, bytes.size() + 1)); + + // Parse the HTTP status header + QRegExp re("HTTP/\\d\\.\\d (\\d+) ([^\r\n]+)"); + headerEnd = data.find("\r\n"); + header = data.left( (headerEnd == -1) ? 20 : headerEnd ); + + re.search(header); + m_statusCode = re.cap(1).toInt(); + m_statusDescription = re.cap(2); + + // Remove the web response status header. + data = data.mid(headerEnd + 2, (data.find("\r\n\r\n") + 2) - (headerEnd + 2)); + // Create a MimeMessage, removing the HTTP status header + m_headers = new MimeMessage(data); + + // Retrieve the contentlength header. + header = m_headers->getValue("Content-Length"); + if(!header.isNull()) + { + bool valid; + int length = header.toInt(&valid); + if(valid && length > 0) + { + // If the content length is valid, and not zero, + // copy the web response content bytes. + int offset = bytes.size() - length; + + QByteArray content(length); + for(int i=0; i < length; i++) + content[i] = bytes[offset + i]; + // Create the web response stream from the response content bytes. + m_stream = new QDataStream(content, IO_ReadOnly); + } + } +} + +MSNSocket::WebResponse::~WebResponse() +{ + delete m_headers; + m_headers = 0; + delete m_stream; + m_stream = 0; +} + +MimeMessage* MSNSocket::WebResponse::getHeaders() +{ + return m_headers; +} + +QDataStream* MSNSocket::WebResponse::getResponseStream() +{ + return m_stream; +} + +int MSNSocket::WebResponse::getStatusCode() +{ + return m_statusCode; +} + +QString MSNSocket::WebResponse::getStatusDescription() +{ + return m_statusDescription; +} + + +#include "msnsocket.moc" + +// vim: set noet ts=4 sts=4 sw=4: + |