/*************************************************************************** offlineimservice.cpp - description ------------------- begin : Thu Sep 07 2006 copyright : (C) 2006 by Diederik van der Boor email : "vdboor" --at-- "codingdomain.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 "offlineimservice.h" #include "../../kmessdebug.h" #include "../../chat/chatmessage.h" #include "../mimemessage.h" #include <qdom.h> #include <kmdcodec.h> #include <krfcdate.h> #ifdef KMESSDEBUG_OFFLINE_IM #define KMESSDEBUG_OFFLINE_IM_GENERAL #endif /** * @brief The constructor. * * Initializes the client for the Offline-IM webservice. * * @param authT The <code>t</code> value of the passport cookie. * @param authP The <code>p</code> value of the passport cookie. * @param parent The Qt parent object, when it's destroyed this class will be cleaned up automatically too. */ 00044 OfflineImService::OfflineImService( const QString &authT, const QString &authP, QObject *parent ) : HttpSoapConnection("https://rsi.hotmail.com/rsi/rsi.asmx", parent, "OfflineImService") , authT_(authT) , authP_(authP) { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "CREATED OfflineImService" << endl; #endif // Initialize the header once. passportCookieHeader_ = " <PassportCookie xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">\n" " <t>" + escapeString(authT) + "</t>\n" " <p>" + escapeString(authP) + "</p>\n" " </PassportCookie>"; } /** * @brief The destructor. */ 00066 OfflineImService::~OfflineImService() { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "DESTROYED OfflineImService" << endl; #endif } /** * @brief SOAP call to delete messages from the remote storage. * @param messageIds List of messages to delete. */ 00079 void OfflineImService::deleteMessages( const QStringList &messageIds ) { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "OfflineImService: requesting deletion of messages: '" << messageIds.join("', '") << "'" << endl; #endif #ifdef KMESSTEST ASSERT( ! messageIds.isEmpty() ); #endif // Initialize request QString soapAction = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/DeleteMessages"; QString soapBody = " <DeleteMessages xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">\n" " <messageIds>\n" " <messageId>" + messageIds.join("</messageId>\n <messageId>") + "</messageId>\n" " </messageIds>\n" " </DeleteMessages>"; sendRequest( soapAction, soapBody, passportCookieHeader_ ); } /** * @brief Internal function to extract the e-mail address from an RFC822 formatted string. * * For a value such as '<code>"Contactname" <contact@hotmail.com></code>', * this method returns '<code>contact@hotmail.com</code>'. * * @param address The string with an e-mail address. * @returns The e-mail address of the contact. */ 00110 QString OfflineImService::extractRFC822Address( const QString &address ) { if( address.endsWith(">") ) { int pos = address.findRev('<'); if( pos != -1 ) { return address.mid( pos + 1, address.length() - pos - 2 ); } } return address; } /** * @brief SOAP call to download an offline message. * * The message ID can be extracted from * the following messages types received at the notification connection: * - <code>text/x-msmsgsoimnotification</code> * - <code>text/x-msmsgsinitialmdatanotification</code> * * The messageReceived() signal is fired when the webservice returns the message. * * @param messageId The ID of the message. * @param markAsRead Whether the message should be marked as read. */ 00139 void OfflineImService::getMessage( const QString &messageId, bool markAsRead ) { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "OfflineImService: requesting message '" << messageId << "'" << endl; #endif // Initialize request QString soapAction = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMessage"; QString soapBody = " <GetMessage xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\">\n" " <messageId>" + escapeString(messageId) + "</messageId>\n" " <alsoMarkAsRead>" + (markAsRead ? "true" : "false") + "</alsoMarkAsRead>\n" " </GetMessage>"; sendRequest( soapAction, soapBody, passportCookieHeader_, messageId ); } /** * @brief SOAP call to download the value of the Mail-Data field. * * The value of the <code>Mail-Data</code> field is normally received by the notification server. * If there are many offline-IM messages, the value of this field is literally <code>too-large</code>. * In that case, the message data can be downloaded using this method. * * The metaDataReceived() signal is fired when the webservice returns the data. */ 00166 void OfflineImService::getMetaData() { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "OfflineImService: requesting Mail-Data field" << endl; #endif QString soapAction = "http://www.hotmail.msn.com/ws/2004/09/oim/rsi/GetMetadata"; QString soapBody = " <GetMetadata xmlns=\"http://www.hotmail.msn.com/ws/2004/09/oim/rsi\" />"; sendRequest( soapAction, soapBody, passportCookieHeader_ ); } /** * @brief Internal function to process the response of the webservice. * * This handles the following response nodes: * - <code>GetMessageResult</code> is handled by processGetMessageResult(). * - <code>GetMetadataResponse</code> is sent with the metaDataReceived() signal. * - <code>DeleteMessagesResponse</code> is ignored because it's always empty. * * Other unrecognized response types result in a warning message at the console. * * @param resultRoot The result root of the SOAP response. */ 00191 void OfflineImService::parseSoapResult( const QDomElement &resultRoot, const QDomElement &/*headerNode*/ ) { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "OfflineImService: parsing SOAP response." << endl; #endif QString resultName = resultRoot.nodeName(); if( resultName == "GetMessageResult" ) { processGetMessageResult( resultRoot ); } else if( resultName == "GetMetadataResponse" ) { emit metaDataReceived( resultRoot.namedItem("MD").toElement() ); } else if( resultName == "DeleteMessagesResponse" ) { // do nothing } else { kdWarning() << "OfflineImService::slotRequestFinished: Unknown response type received " << "(rootnode=" << resultName << " endpoint=" << getEndpoint() << ")" << endl; } } /** * @brief Internal function to process the response of the getMessage() call. * * This method parses the XML data, extracts the relevant fields, * and fires the messageReceived() signal with the results. * * @param resultRoot The <code>GetMessageResult</code> node of the response. */ 00227 void OfflineImService::processGetMessageResult( const QDomElement &resultRoot ) { #ifdef KMESSDEBUG_OFFLINE_IM_GENERAL kdDebug() << "OfflineImService: parsing downloaded Offline-IM message." << endl; #endif // Parse as MIME message. MimeMessage message( resultRoot.text() ); // Extract the fields QString from = message.decodeRFC2047String( message.getValue("From").data() ); QString to = message.decodeRFC2047String( message.getValue("To").data() ); QString dateString = message.getValue("Date"); QString runId = message.getValue("X-OIM-Run-Id"); int sequenceNum = message.getValue("X-OIM-Sequence-Num").toInt(); // Convert the 'From' and 'To' lines, it's something like "Contactname <contact@hotmail.com>" from = extractRFC822Address( from ); to = extractRFC822Address( to ); // Convert the date, is something like "15 Nov 2005 14:24:27 -0800" QDateTime date; date.setTime_t( KRFCDate::parseDate( dateString ) ); // Validate content type QString contentType = message.getValue("Content-Type"); if( contentType.lower() != "text/plain; charset=utf-8" ) { kdWarning() << "OfflineImService: received unexpected content type: " << contentType << endl; } // Validate transfer encoding QString transferEncoding = message.getValue("Content-Transfer-Encoding"); if( transferEncoding != "base64" ) { kdWarning() << "OfflineImService: received unexpected transfer encoding: " << transferEncoding << endl; } // Validate message type QString messageType = message.getValue("X-OIM-Message-Type"); if( messageType != "OfflineMessage" ) { kdWarning() << "OfflineImService: received unexpected message type: " << messageType << endl; } // Process the body QCString decodedBody = KCodecs::base64Decode( message.getBody().utf8() ); QString body = QString::fromUtf8( decodedBody.data(), decodedBody.length() ); // Return to caller // The MessageID was stored in the "RequestId" field of the sendRawRequest() method emit messageReceived( getRequestId(), from, to, date, body, runId, sequenceNum ); } #include "offlineimservice.moc"