From: <fon...@us...> - 2010-05-16 11:26:45
|
Revision: 6009 http://kmess.svn.sourceforge.net/kmess/?rev=6009&view=rev Author: fontknocker Date: 2010-05-16 11:26:38 +0000 (Sun, 16 May 2010) Log Message: ----------- Lots of changes to the ABservice, mostly focused on cache logic and moving to the binding. Moved ListsManager update logic into ABService from NSConnection. Yahoo! contact support improved. ChangeLog has the other changes. Modified Paths: -------------- trunk/kmess/lib/ChangeLog trunk/kmess/lib/include/KMess/msncontact.h trunk/kmess/lib/include/KMess/msnsession.h trunk/kmess/lib/src/CMakeLists.txt trunk/kmess/lib/src/connections/msnnotificationconnection.cpp trunk/kmess/lib/src/connections/msnnotificationconnection.h trunk/kmess/lib/src/debug/debugger.cpp trunk/kmess/lib/src/listsmanager.cpp trunk/kmess/lib/src/listsmanager.h trunk/kmess/lib/src/msnsession.cpp trunk/kmess/lib/src/soap/addressbookservice.cpp trunk/kmess/lib/src/soap/addressbookservice.h trunk/kmess/lib/src/soap/bindings/abbinding.h trunk/kmess/lib/src/soap/bindings/abdatatypes.cpp trunk/kmess/lib/src/soap/bindings/abdatatypes.h trunk/kmess/lib/src/soap/bindings/abrequests.cpp trunk/kmess/lib/src/soap/bindings/abrequests.h Modified: trunk/kmess/lib/ChangeLog =================================================================== --- trunk/kmess/lib/ChangeLog 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/ChangeLog 2010-05-16 11:26:38 UTC (rev 6009) @@ -3,6 +3,16 @@ * Start adding a simple XML binding for the address book service. Will make it easier to work with the service plus make it easier to put in Yahoo! and other network support. + * MsnNotificationConnection: moved the listsManager update logic to ABService since + that's probably where it belongs. Renamed createAddressBookService to addressBookService. + * ABService: implement the basics of Membership list caching as well. Now takes a cache directory + instead of cache file. Shifted a few methods over to using the AB binding. Added fetchAddressBook + method which handles the retrieval of membership and addressbook. Refactored the cache logic to reduce + duplication. + * ABService: add better Yahoo! contact support through supporting EmailMember as well as PassportMember. + * CMakeLists.txt: build and link the AB binding. + * Fix some doxygen warnings. + * abdatatypes.cpp: Fix a couple of bugs in the binding (element names). 2010-05-05 Sjors Gielen <sj...@km...> Modified: trunk/kmess/lib/include/KMess/msncontact.h =================================================================== --- trunk/kmess/lib/include/KMess/msncontact.h 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/include/KMess/msncontact.h 2010-05-16 11:26:38 UTC (rev 6009) @@ -29,6 +29,7 @@ { class ListsManager; class MsnNotificationConnection; + class AddressBookService; }; namespace KMess @@ -57,8 +58,8 @@ friend class KMessInternal::ListsManager; friend class KMessInternal::MsnNotificationConnection; friend class MsnSession; + friend class KMessInternal::AddressBookService; - public: // Public methods const ClientCapabilities capabilities() const; QHash<QUuid,MPOPEndpoint*> endpoints() const; Modified: trunk/kmess/lib/include/KMess/msnsession.h =================================================================== --- trunk/kmess/lib/include/KMess/msnsession.h 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/include/KMess/msnsession.h 2010-05-16 11:26:38 UTC (rev 6009) @@ -139,7 +139,7 @@ // Get the logon mode LogonMode logonMode() const; // Set the cache file for contact list data - void setCacheFile( const QString &file ); + void setCacheDir( const QString &path ); // Alter the quantity of unread email void setEmailCount( int change ); // Set the current personal message Modified: trunk/kmess/lib/src/CMakeLists.txt =================================================================== --- trunk/kmess/lib/src/CMakeLists.txt 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/CMakeLists.txt 2010-05-16 11:26:38 UTC (rev 6009) @@ -41,6 +41,10 @@ soap/passportloginservice.cpp soap/roamingservice.cpp soap/soapmessage.cpp + soap/bindings/abrequests.cpp + soap/bindings/abdatatypes.cpp + soap/bindings/soaputils.cpp + soap/bindings/abresponse.cpp # utils/upnp/igdcontrolpoint.cpp # utils/upnp/layer3forwardingservice.cpp Modified: trunk/kmess/lib/src/connections/msnnotificationconnection.cpp =================================================================== --- trunk/kmess/lib/src/connections/msnnotificationconnection.cpp 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/connections/msnnotificationconnection.cpp 2010-05-16 11:26:38 UTC (rev 6009) @@ -156,7 +156,7 @@ { debug() << "adding '" << handle << " to '" << groupId << "'."; - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); MsnContact *contact = listsManager_->contact( handle ); KMESS_NULL( contact ); @@ -183,7 +183,7 @@ debug() << "Re-adding contact " << handle; // Request the re-adding of contact to AB - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->addContact( handle, groups , true ); } @@ -202,7 +202,7 @@ debug() << "Adding contact " << handle; // Request the adding of contact to AB - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->addContact( handle, groups ); } @@ -236,7 +236,7 @@ putAdl( handle, MSN_LIST_ALLOWED ); - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->unblockContact( handle ); } @@ -251,7 +251,7 @@ */ void MsnNotificationConnection::groupAdd( const QString& name ) { - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->addGroup( name ); } @@ -282,7 +282,7 @@ putRml( handle, MSN_LIST_ALLOWED ); putAdl( handle, MSN_LIST_BLOCKED ); - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->blockContact( handle ); } @@ -334,7 +334,7 @@ changeProperty( "MFN", newName ); - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->contactUpdate( AddressBookService::PROPERTY_FRIENDLYNAME, newName ); } @@ -544,11 +544,11 @@ * @brief Create and return one pointer to address book service. * */ -AddressBookService* MsnNotificationConnection::createAddressBookService() +AddressBookService* MsnNotificationConnection::addressBookService() { if( addressBookService_ == 0 ) { - addressBookService_ = new AddressBookService( session_, this ); + addressBookService_ = new AddressBookService( session_, listsManager_, this ); addSoapClient( addressBookService_ ); // Contact actions signals/slots @@ -578,11 +578,13 @@ this, SLOT ( slotGroupRenamed(QString,QString) ) ); // Membership and AddressBook signals/slots - connect( addressBookService_, SIGNAL( gotMembershipLists(QString,QHash<QString,KMess::MsnMemberships>) ), - this, SLOT ( slotGotMembershipLists(QString,QHash<QString,KMess::MsnMemberships>) ) ); - connect( addressBookService_, SIGNAL( gotAddressBookList(QList<ABContactInfo>) ), - this, SLOT ( slotGotAddressBookList(QList<ABContactInfo>) ) ); - +// connect( addressBookService_, SIGNAL( gotMembershipLists(QString,QHash<QString,KMess::MsnMemberships>) ), +// this, SLOT ( slotGotMembershipLists(QString,QHash<QString,KMess::MsnMemberships>) ) ); +// connect( addressBookService_, SIGNAL( gotAddressBookList(QList<ABContactInfo>) ), +// this, SLOT ( slotGotAddressBookList(QList<ABContactInfo>) ) ); + connect( addressBookService_, SIGNAL( addressBookParsed() ), + this, SLOT ( slotAddressBookParsed() ) ); + connect( addressBookService_, SIGNAL( gotGroup(QString,QString) ), this, SLOT ( slotGotGroup(QString,QString) ) ); @@ -1158,7 +1160,10 @@ } // Update contact status. - MsnContact *contact = session_->getContact( idString ); + MsnContact *contact = listsManager_->contact( idString ); + + KMESS_ASSERT( contact ); + MsnStatus lastStatus = contact->status(); contact->setStatus( status ); @@ -1483,12 +1488,10 @@ warning() << "Passport account of the user is not verified yet."; } - emit statusEvent( KMess::TYPE_PROGRESS, KMess::MESSAGE_PROGRESS_GETTING_MEMBERSHIPS ); - - // Retrieve from with one SOAP request the membership list ( where there are the lists for - // FL AL BL contacts ) - AddressBookService *addressBook = createAddressBookService(); - addressBook->retrieveMembershipLists(); + emit statusEvent( KMess::TYPE_PROGRESS, KMess::MESSAGE_PROGRESS_GETTING_CONTACT_LIST ); + + // download the address book and memberships. + addressBookService()->fetchAddressBook( cacheDir_ ); } else { @@ -1803,7 +1806,7 @@ { debug() << "Moving '" << handle << "' from '" << fromGroupId << "' to '" << toGroupId << "'."; - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); MsnContact *contact = listsManager_->contact( handle ); KMESS_NULL( contact ); @@ -2329,7 +2332,7 @@ } // Remove the contact from AB - AddressBookService *addressBook = createAddressBookService();; + AddressBookService *addressBook = addressBookService();; addressBook->deleteContact( contactId ); // if we're not blocking, make sure we force an add to the allow @@ -2345,7 +2348,7 @@ //remove a contact from a single group void MsnNotificationConnection::contactRemoveFromGroup( const QString &handle, const QString &groupId ) { - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); MsnContact *contact = listsManager_->contact( handle ); KMESS_NULL( contact ); @@ -2357,7 +2360,7 @@ // Remove a group void MsnNotificationConnection::groupRemove( const QString &id ) { - AddressBookService *addressBook = createAddressBookService();; + AddressBookService *addressBook = addressBookService();; addressBook->deleteGroup( id ); } @@ -2366,7 +2369,7 @@ // Rename a group void MsnNotificationConnection::groupRename( const QString& id, const QString& newName ) { - AddressBookService *addressBook = createAddressBookService();; + AddressBookService *addressBook = addressBookService();; addressBook->renameGroup( id, newName ); } @@ -2457,12 +2460,12 @@ /** - * Set the path to the cache file. - * @param file The path to the cache file. + * Set the path to the cache directory. + * @param path The path to the cache directory. */ -void MsnNotificationConnection::setCacheFile( const QString &file ) +void MsnNotificationConnection::setCacheDir( const QString &path ) { - cacheFile_ = file; + cacheDir_ = path; } @@ -2514,7 +2517,8 @@ // The contact is added to the reverse list: when the user adds a contact who has added the user, // the user must appear to be present in the contact's list (this avoids seeing the newly added // contact as not having the user in his/her list). - listsManager_->contactAdd( handle, handle, lists | MSN_LIST_REVERSE, QStringList(), contactId ); + #warning TODO: NetworkYahoo. + listsManager_->contactAdd( handle, handle, lists | MSN_LIST_REVERSE, QStringList(), contactId, KMess::NetworkMsn ); contact = listsManager_->contact( handle ); } @@ -2598,52 +2602,13 @@ -void MsnNotificationConnection::slotGotAddressBookList( const QList<ABContactInfo> &contacts ) -{ - debug() << "Parsing address book..."; +/* +Called when the AB service informs us it's completed retrieval and parsing of the address book. - // Stop the login timer to allow parsing the address book. - loginTimer_.stop(); - - QListIterator<ABContactInfo> contact( contacts ); - KMess::MsnMemberships lists; - - // Cycle for each contact - while( contact.hasNext() ) - { - // Grep the main informations - ABContactInfo contactInfo( contact.next() ); - - // If we have the contact in the AB then it is our friend. - lists = contactsRole_.take( contactInfo.handle ) | MSN_LIST_FRIEND; - - // add to contact list model - listsManager_->contactAdd( contactInfo.handle, contactInfo.friendlyName, lists, contactInfo.groupList, contactInfo.contactId ); - - MsnContact *contact = listsManager_->contact( contactInfo.handle ); - KMESS_NULL( contact ); - } - - // If there are contacts not present in AB, but present in Membership List - // add them to contactlist - if( contactsRole_.count() > 0 ) - { - QHashIterator< QString, KMess::MsnMemberships > i( contactsRole_ ); - while( i.hasNext() ) - { - i.next(); - const QString& handle( i.key() ); - lists = i.value(); - listsManager_->contactAdd( handle, handle, lists, QStringList(), QString() ); - } - } - - // Allow some time until the next message is received - if( loginTimer_.isActive() ) - { - loginTimer_.start(); - } - +Here we sent the initial ADL. +*/ +void MsnNotificationConnection::slotAddressBookParsed() +{ emit statusEvent( KMess::TYPE_PROGRESS, KMess::MESSAGE_PROGRESS_UPDATING_SERVER ); putAdl(); @@ -2657,30 +2622,30 @@ * In this list there are informations about FL, AL, BL lists. * */ -void MsnNotificationConnection::slotGotMembershipLists( const QString &serviceType, const QHash<QString,KMess::MsnMemberships> &contactsRole ) -{ - if( serviceType != "Messenger" ) - { - warning() << "Unable to parse membership list:" << serviceType; - return; - } +// void MsnNotificationConnection::slotGotMembershipLists() +// { +// if( serviceType != "Messenger" ) +// { +// warning() << "Unable to parse membership list:" << serviceType; +// return; +// } +// +// contactsRole_ = contactsRole; +// +// emit statusEvent( KMess::TYPE_PROGRESS, KMess::MESSAGE_PROGRESS_GETTING_CONTACT_LIST ); +// +// // Allow some time until the next message is received +// if( loginTimer_.isActive() ) +// { +// loginTimer_.start(); +// } +// +// AddressBookService *addressBook = addressBookService();; +// addressBook->retrieveAddressBook( cacheFile_ ); +// } - contactsRole_ = contactsRole; - emit statusEvent( KMess::TYPE_PROGRESS, KMess::MESSAGE_PROGRESS_GETTING_CONTACT_LIST ); - // Allow some time until the next message is received - if( loginTimer_.isActive() ) - { - loginTimer_.start(); - } - - AddressBookService *addressBook = createAddressBookService();; - addressBook->retrieveAddressBook( cacheFile_ ); -} - - - /** * @brief Called when address book services parsed a group. * @@ -2754,7 +2719,7 @@ putRml( handle, MSN_LIST_BLOCKED ); putAdl( handle, MSN_LIST_ALLOWED ); - AddressBookService *addressBook = createAddressBookService(); + AddressBookService *addressBook = addressBookService(); addressBook->unblockContact( handle ); } Modified: trunk/kmess/lib/src/connections/msnnotificationconnection.h =================================================================== --- trunk/kmess/lib/src/connections/msnnotificationconnection.h 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/connections/msnnotificationconnection.h 2010-05-16 11:26:38 UTC (rev 6009) @@ -127,6 +127,8 @@ MsnNotificationConnection( KMess::MsnSession* session, ListsManager* listsManager ); // The destructor ~MsnNotificationConnection(); + // Create and return one pointer to address book service + AddressBookService *addressBookService(); // Close the connection with the server void closeConnection(); // Open a connection to the server @@ -172,7 +174,7 @@ // send a UUN command void sendUUN( const QString &handle, int type, const QString &body ); // Set the cache file - void setCacheFile( const QString &file ); + void setCacheDir( const QString &path ); public slots: // Change the personal message of the user. @@ -189,8 +191,6 @@ private: // Private methods - // Create and return one pointer to address book service - AddressBookService *createAddressBookService(); // Create the Offline-IM service OfflineImService *createOfflineImService(); // Create the passport login handler @@ -275,9 +275,7 @@ // Contact removed from group void slotContactDeletedFromGroup( const QString &contactId, const QString &groupId ); // Called when address book services retrieved address book list - void slotGotAddressBookList( const QList<ABContactInfo> &contacts ); - // Called when address book services retrieved membership lists - void slotGotMembershipLists( const QString &serviceType, const QHash<QString,KMess::MsnMemberships> &contactsRole ); + void slotAddressBookParsed(); // Called when address book services parsed a group void slotGotGroup( const QString &id, const QString &name ); // Called when a group is added @@ -310,7 +308,7 @@ // The address book service AddressBookService *addressBookService_; // The cache file path - QString cacheFile_; + QString cacheDir_; // The list of contact roles QHash<QString,KMess::MsnMemberships> contactsRole_; // Whether or not the connection has just been established Modified: trunk/kmess/lib/src/debug/debugger.cpp =================================================================== --- trunk/kmess/lib/src/debug/debugger.cpp 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/debug/debugger.cpp 2010-05-16 11:26:38 UTC (rev 6009) @@ -374,7 +374,7 @@ // If the SOAP service we want to (ab)use is not started, start it if( ! service ) { - service = session_->msnNotificationConnection_->createAddressBookService(); + service = session_->msnNotificationConnection_->addressBookService(); } // It will be deleted when the SOAP class is done Modified: trunk/kmess/lib/src/listsmanager.cpp =================================================================== --- trunk/kmess/lib/src/listsmanager.cpp 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/listsmanager.cpp 2010-05-16 11:26:38 UTC (rev 6009) @@ -69,7 +69,7 @@ * tells us that the new contact is now in our list. */ void ListsManager::contactAdd( const QString &handle, const QString &friendlyName, int lists, - QStringList groupIds, const QString &guid ) + QStringList groupIds, const QString &guid, KMess::NetworkType network ) { KMESS_ASSERT( ! handle.isEmpty() ); @@ -80,43 +80,41 @@ return; } - MsnContact *contact; + MsnContact *contact = 0; - // is this contact an "unknown" contact that the user had added? - if ( unknownContacts_.contains( handle ) ) + if ( handle == session_->getHandle() ) { - contact = unknownContacts_.take( handle ); - - contact->setGroupIds( groupIds ); - contact->setMemberships( (MsnMemberships)lists ); - contact->setFriendlyName( friendlyName ); - contact->setGuid( guid ); - - // it's now a known contact since we set non-zero memberships. - } - else if ( handle == session_->getHandle() ) - { // it's ourselves. self_->setMemberships( (MsnMemberships)lists ); self_->setFriendlyName( friendlyName ); self_->setGroupIds( groupIds ); self_->setGuid( guid ); + self_->setNetwork( KMess::NetworkMsn ); // always, duh. contact = self_; + + model_->contactAdd( contact ); + return; } - else + + if ( unknownContacts_.contains( handle ) ) { + contact = unknownContacts_.take( handle ); + } + else + { // Create the new contact contact = new MsnContact( handle, friendlyName, lists, groupIds, guid ); } - // don't add self_ into the contacts_ hash. manage it separately so - // we can keep track of it properly. add it into the model though. - if ( contact != self_ ) - { - contacts_.insert( handle, contact ); + contact->setGroupIds( groupIds ); + contact->setMemberships( (MsnMemberships)lists ); + contact->setFriendlyName( friendlyName ); + contact->setGuid( guid ); + contact->setNetwork( network ); + + contacts_.insert( handle, contact ); - debug() << "Adding to list contact" << handle << "within groups" << groupIds; - } + debug() << "Adding to list contact" << handle << "within groups" << groupIds; // Also add it to the model model_->contactAdd( contact ); Modified: trunk/kmess/lib/src/listsmanager.h =================================================================== --- trunk/kmess/lib/src/listsmanager.h 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/listsmanager.h 2010-05-16 11:26:38 UTC (rev 6009) @@ -62,7 +62,7 @@ public slots: void contactAdd( const QString& handle, const QString& friendlyName, - int lists, QStringList groupIds, const QString& guid ); + int lists, QStringList groupIds, const QString& guid, KMess::NetworkType network ); void contactReadded( const QString &handle ); void contactAddToGroup( const QString& handle, const QString& groupId ); void contactRemoveFromGroup( const QString& handle, const QString& groupId ); Modified: trunk/kmess/lib/src/msnsession.cpp =================================================================== --- trunk/kmess/lib/src/msnsession.cpp 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/msnsession.cpp 2010-05-16 11:26:38 UTC (rev 6009) @@ -347,18 +347,19 @@ /** - * Set the full path to the file that will be used to read/write address book - * cache information. + * Set the full path to the directory that will store address book cache information. * * It is a very good idea to use this feature since it will result in quicker sign in times. * This is because libkmess-msn does not have to read the entire contact list from the server each time, rather, * it only requests the most recent changes. * - * @param file The full path to the caching file. + * If the cache directory does not exist, it will be created. + * + * @param path The full path to the caching directory. */ -void MsnSession::setCacheFile( const QString &file ) +void MsnSession::setCacheDir( const QString &path ) { - msnNotificationConnection_->setCacheFile( file ); + msnNotificationConnection_->setCacheDir( path ); } Modified: trunk/kmess/lib/src/soap/addressbookservice.cpp =================================================================== --- trunk/kmess/lib/src/soap/addressbookservice.cpp 2010-05-16 04:27:59 UTC (rev 6008) +++ trunk/kmess/lib/src/soap/addressbookservice.cpp 2010-05-16 11:26:38 UTC (rev 6009) @@ -17,6 +17,7 @@ #include "debug/libkmessdebug.h" #include "addressbookservice.h" +#include "../listsmanager.h" #include <KMess/MsnSession> #include <KMess/NetworkGlobals> @@ -28,6 +29,7 @@ #include <QDomNode> #include <QFile> #include <QTextCodec> +#include <QDir> #include "../utils/xmlfunctions.h" #include "soapmessage.h" @@ -52,13 +54,15 @@ #define SERVICE_URL_ADDRESSBOOK_SHARING "https://omega.contacts.msn.com/abservice/SharingService.asmx" +#define CONTACTS_API "Contacts" /** * @brief The constructor */ -AddressBookService::AddressBookService( KMess::MsnSession *session, QObject *parent ) +AddressBookService::AddressBookService( KMess::MsnSession *session, ListsManager *listsManager, QObject *parent ) : PassportLoginService( session, parent ) { + listsManager_ = listsManager; } @@ -160,36 +164,15 @@ void AddressBookService::addGroup( const QString &name ) { - QString body( "<ABGroupAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <groupAddOptions>\n" - " <fRenameOnMsgrConflict>false</fRenameOnMsgrConflict>\n" - " </groupAddOptions>\n" - " <groupInfo>\n" - " <GroupInfo>\n" - " <name>" + KMess::Utils::htmlEscape( name ) + "</name>\n" - " <groupType>C8529CE2-6EAD-434d-881F-341E17DB3FF8</groupType>\n" - " <fMessenger>false</fMessenger>\n" - " <annotations>\n" - " <Annotation>\n" - " <Name>MSN.IM.Display</Name>\n" - " <Value>1</Value>\n" - " </Annotation>\n" - " </annotations>\n" - " </GroupInfo>\n" - " </groupInfo>\n" - "</ABGroupAdd>" ); - + ABGroupAddRequest req; + req.groupInfo.name = KMess::Utils::htmlEscape( name ); + req.groupInfo.annotations.append( new ABAnnotation( "MSN.IM.Display", "1" ) ); + MessageData data; data.type = "GroupAdd"; data.value = name; - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABGroupAdd", - createCommonHeader( "GroupSave" ), - body, - data ), - "Contacts" ); + sendSecureRequest( req.message( data ), CONTACTS_API ); } @@ -218,20 +201,10 @@ */ void AddressBookService::createAddressBook() { - QString body( "<ABAdd xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abInfo>\n" - " <name/>\n" - " <ownerPuid>0</ownerPuid>\n" - " <ownerEmail>" + session_->getSessionSettingString( "AccountHandle" ) + "</ownerEmail>\n" - " <fDefault>true</fDefault>\n" - " </abInfo>\n" - "</ABAdd>" ); - - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABAdd", - createCommonHeader(), - body ), - "Contacts" ); + ABAddRequest req; + req.abInfo.ownerEmail = session_->getSessionSettingString( "AccountHandle" ); + + sendSecureRequest( req.message(), CONTACTS_API ); } @@ -271,129 +244,174 @@ QString propertyString; QString propertiesChanged; + ABContactUpdateRequest req; + + ABContact *c = new ABContact(); + c->contactInfo.contactType = "Me"; + switch( property ) { case PROPERTY_FRIENDLYNAME: - propertyString = " <displayName>" + KMess::Utils::htmlEscape( newValue ) + "</displayName>\n"; - propertiesChanged = "DisplayName"; + c->contactInfo.displayName = KMess::Utils::htmlEscape( newValue ); + c->propertiesChanged = "DisplayName"; break; case PROPERTY_PRIVACY: - propertyString = ( " <annotations>\n" - " <Annotation>\n" - " <Name>MSN.IM.BLP</Name>\n" - " <Value>" + newValue + "</Value>\n" - " </Annotation>\n" - " </annotations>\n"); - propertiesChanged = "Annotation"; + c->contactInfo.annotations.append( new ABAnnotation( "MSN.IM.BLP", newValue ) ); + c->propertiesChanged = "Annotation"; break; default: return; } - QString body( "<ABContactUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <contacts>\n" - " <Contact xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <contactInfo>\n" - " <contactType>Me</contactType>\n" - + propertyString + - " </contactInfo>\n" - " <propertiesChanged>" + propertiesChanged + "</propertiesChanged>\n" - " </Contact>\n" - " </contacts>\n" - "</ABContactUpdate>" ); - - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABContactUpdate", - createCommonHeader( "Timer" ), - body ), - "Contacts" ); + req.contacts.append( c ); + + sendSecureRequest( req.message(), CONTACTS_API ); } void AddressBookService::deleteContact( const QString &contactId ) { - QString body( "<ABContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <contacts>\n" - " <Contact>\n" - " <contactId>" + contactId + "</contactId>\n" - " </Contact>\n" - " </contacts>\n" - "</ABContactDelete>" ); - + ABContactDeleteRequest req; + ABContact *c = new ABContact(); + c->contactId = contactId; + + req.contacts.append( c ); + MessageData data; data.type = "ContactDelete"; data.value = contactId; - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABContactDelete", - createCommonHeader( "Timer" ), - body, - data ), - "Contacts" ); + sendSecureRequest( req.message( data ), CONTACTS_API ); } void AddressBookService::deleteContactFromGroup( const QString &contactId, const QString &groupId ) { - QString body( "<ABGroupContactDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <groupFilter>\n" - " <groupIds>\n" - " <guid>" + groupId + "</guid>\n" - " </groupIds>\n" - " </groupFilter>\n" - " <contacts>\n" - " <Contact>\n" - " <contactId>" + contactId + "</contactId>\n" - " </Contact>\n" - " </contacts>\n" - "</ABGroupContactDelete>" ); - + ABGroupContactDeleteRequest req; + req.groupIds.append( groupId ); + + ABContact *c = new ABContact(); + c->contactId = contactId; + req.contacts.append( c ); + MessageData data; data.type = "ContactDeleteFromGroup"; data.value = QStringList() << contactId << groupId; - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABGroupContactDelete", - createCommonHeader( "GroupSave" ), - body, - data ), - "Contacts" ); + sendSecureRequest( req.message( data ), CONTACTS_API ); } void AddressBookService::deleteGroup( const QString &groupId ) { - QString body( "<ABGroupDelete xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <groupFilter>\n" - " <groupIds>\n" - " <guid>" + groupId + "</guid>\n" - " </groupIds>\n" - " </groupFilter>\n" - "</ABGroupDelete>" ); - + ABGroupDeleteRequest req; + + req.groupIds.append( groupId ); + MessageData data; data.type = "GroupDelete"; data.value = groupId; - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABGroupDelete", - createCommonHeader( "GroupSave" ), - body, - data ), - "Contacts" ); + sendSecureRequest( req.message( data ), CONTACTS_API ); } +/** + * Retrieves the address book and memberships from the server and caches them + * in the given cachedir. + * + * @param cacheDir Directory for membership/address book cache data. + */ +void AddressBookService::fetchAddressBook( const QString &cacheDir ) +{ + // ensure the cache is usable. + QDir dir( cacheDir ); + + if ( ! dir.exists() ) + { + QDir temp( dir ); + temp.cdUp(); // up to the parent... + temp.mkpath( dir.dirName() ); // create the cache folder. + } + + if ( ! cacheDir.isEmpty() && dir.exists() ) + { + membershipCache_.cacheFile = cacheDir + "/membership_cache"; + addressbookCache_.cacheFile = cacheDir + "/addressbook_cache"; + } + else + { + warning() << "Cache dir" << cacheDir << "does not exist or is unreadable. Caching will not be used."; + membershipCache_.cacheFile = addressbookCache_.cacheFile = QString(); + } + + // once we get the membership lists and parse them + // parseMembershipLists() will call retrieveAdddressBook(). + retrieveMembershipLists(); +} + + + +/** + * Reads the address book cache file and loads it into addressBookCache_.cacheDocument. + */ +void AddressBookService::readCache( ABCacheData &data, const QString &lastChangePath ) +{ + if ( data.cacheFile.isEmpty() ) + { + return; + } + + QFile fCache( data.cacheFile ); + + if ( ! fCache.open( QIODevice::ReadOnly ) ) + { + return; + } + + // cache is open. read. + if ( ! data.cacheDocument.setContent( &fCache ) ) + { + warning() << "Cache file" << data.cacheFile << "could not be parsed."; + return; + } + + debug() << "Cache file" << data.cacheFile << "opened and parsed OK."; + + if ( data.cacheDocument.isNull() ) + { + // cache is either empty or invalid. + return; + } + + // get a timestamp out of it if possible. + QString cacheTS = XmlFunctions::getNodeValue( data.cacheDocument.documentElement(), lastChangePath ); + + // QDateTime doesn't handle the "-8:00" that MS adds to the end of the ISODate. + data.cacheTimestamp = QDateTime::fromString( cacheTS.left( cacheTS.lastIndexOf("-") ), Qt::ISODate ); + data.cacheTimezone = cacheTS.mid( cacheTS.lastIndexOf("-") ); +} + + + +void AddressBookService::mergeMembershipDeltas( const QDomElement &deltas ) +{ + QDomDocument &cache = membershipCache_.cacheDocument; + + if ( cache.documentElement().isNull() ) + { + cache.appendChild( cache.createProcessingInstruction( "xml", "version='1.0' encoding='utf-8'" ) ); + cache.appendChild( deltas ); + return; + } +} + /* This method merges the cached contact list data with the deltas retrieved from the SOAP server. @@ -404,19 +422,21 @@ As a last step, we simply copy the <ab> node from the server SOAP and replace our local one. That updates our cache timestamp for us. */ -void AddressBookService::mergeCacheDeltas( const QDomElement &deltas ) +void AddressBookService::mergeABDeltas( const QDomElement &deltas ) { + QDomDocument &cache = addressbookCache_.cacheDocument; + // ok, here we go. first step: create the root elements if need be. - if ( cacheDoc_.documentElement().isNull() ) + if ( cache.documentElement().isNull() ) { // the cache is invalid. which should mean that the deltas are in fact the entire contact list. // so let's do this: - cacheDoc_.appendChild( cacheDoc_.createProcessingInstruction( "xml", "version='1.0' encoding='utf-8'" ) ); - cacheDoc_.appendChild( deltas ); + cache.appendChild( cache.createProcessingInstruction( "xml", "version='1.0' encoding='utf-8'" ) ); + cache.appendChild( deltas ); return; // done. } - QDomElement cacheRoot = cacheDoc_.documentElement().firstChild().firstChild().toElement(); // ABFindResponse + QDomElement cacheRoot = cache.documentElement().toElement(); // ABFindAllResult QDomNode cacheContactRoot = cacheRoot.namedItem( "contacts" ); QDomNode cacheGroupRoot = cacheRoot.namedItem( "groups" ); @@ -539,71 +559,22 @@ -// read the cache file from disk and store the result. -void AddressBookService::readCache( const QString &cacheFile ) -{ - if ( cacheFile.isEmpty() ) - { - return; - } - - cacheFile_ = cacheFile; - - QFile fCache( cacheFile ); - - if ( ! fCache.open( QIODevice::ReadOnly ) ) - { - return; - } - - // cache is open. read. - if ( ! cacheDoc_.setContent( &fCache ) ) - { - warning() << "Cache file data could not be parsed; address book caching will not be used."; - return; - } - - debug() << "Cache file" << cacheFile << "opened and parsed OK."; - - // get a timestamp out of it if possible. - QString cacheTS = XmlFunctions::getNodeValue( cacheDoc_.documentElement(), "/ABFindAllResponse/ABFindAllResult/ab/lastChange" ); - - // QDateTime doesn't handle the "-8:00" that MS adds to the end of the ISODate. - cacheTimestamp_ = QDateTime::fromString( cacheTS.left( cacheTS.lastIndexOf("-") ), Qt::ISODate ); - cacheTimezone_ = cacheTS.mid( cacheTS.lastIndexOf("-") ); - - // now we're done. - // retrieveAddressBook() will use the cacheTimestamp_ to request deltas from the server (if the timestamp is valid). - // parseAddressBook() will use the deltas to adjust the cached data before informing the NS connection. -} - - - void AddressBookService::renameGroup( const QString &groupId, const QString &name ) { - QString body( "<ABGroupUpdate xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <groups>\n" - " <Group>\n" - " <groupId>" + groupId + "</groupId>\n" - " <groupInfo>\n" - " <name>" + KMess::Utils::htmlEscape( name ) + "</name>\n" - " </groupInfo>\n" - " <propertiesChanged>GroupName</propertiesChanged>\n" - " </Group>\n" - " </groups>\n" - "</ABGroupUpdate>" ); - + ABGroupUpdateRequest req; + + ABGroup *g = new ABGroup(); + g->groupId = groupId; + g->groupInfo.name = KMess::Utils::htmlEscape( name ); + g->propertiesChanged = "GroupName"; + + req.groups.append( g ); + MessageData data; data.type = "GroupRename"; data.value = QStringList() << groupId << name; - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABGroupUpdate", - createCommonHeader( "GroupSave" ), - body, - data ), - "Contacts" ); + sendSecureRequest( req.message( data ), CONTACTS_API ); } @@ -620,43 +591,21 @@ </code> * It goes on with the headers as usual. Must be tested, I don't know if it works, or * if the response is identical to that of the current AbFindAll. - * - * - * If the cacheFile parameter is non-null, the method first retrieves cached AB data from the - * file and stores it. When the reply is received from the server the deltas are merged into the - * cached data, producing the "new" contact list. This file is then re-cached in the cacheFile. - * - * @param cacheFile The path to a cache file for contact list SOAP data. */ -void AddressBookService::retrieveAddressBook( const QString &cacheFile ) +void AddressBookService::retrieveAddressBook() { // step 1: load the cache data. - readCache( cacheFile ); + readCache( addressbookCache_, "/ABFindAllResponse/ABFindAllResult/ab/lastChange" ); - // now with the cache data, check the cacheTimestamp... + ABFindAllRequest req; - QString body( "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <abView>Full</abView>\n" ); - - if( ! cacheTimestamp_.isValid() ) + if ( addressbookCache_.cacheTimestamp.isValid() ) { - body += " <deltasOnly>false</deltasOnly>\n" - " <lastChange>0001-01-01T00:00:00.0000000-08:00</lastChange>\n"; + req.deltasOnly = true; + req.lastChange = addressbookCache_.cacheTimestamp.toString( Qt::ISODate ) + addressbookCache_.cacheTimezone; } - else - { - body += " <deltasOnly>true</deltasOnly>\n" - " <lastChange>" + cacheTimestamp_.toString( Qt::ISODate ) + cacheTimezone_ + "</lastChange>\n"; - } - - body += "</ABFindAll>"; - - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABFindAll", - createCommonHeader(), - body ), - "Contacts" ); + + sendSecureRequest( req.message(), CONTACTS_API ); } @@ -666,26 +615,16 @@ */ void AddressBookService::retrieveMembershipLists() { - QString body( "<FindMembership xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <serviceFilter xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <Types xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Messenger</ServiceType>\n" -// TODO: Adding more WLM features may require retrieval of the membership lists for these services -/* - " <ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Invitation</ServiceType>\n" - " <ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">SocialNetwork</ServiceType>\n" - " <ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Space</ServiceType>\n" - " <ServiceType xmlns=\"http://www.msn.com/webservices/AddressBook\">Profile</ServiceType>\n" -*/ - " </Types>\n" - " </serviceFilter>\n" - "</FindMembership>" ); + readCache( membershipCache_, "/FindMembershipResponse/FindMembershipResult/OwnerNamespace/LastChange" ); - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK_SHARING, - "http://www.msn.com/webservices/AddressBook/FindMembership", - createCommonHeader(), - body ), - "Contacts" ); + ABFindMembershipRequest req; + req.serviceTypes.append( "Messenger" ); +// req.serviceTypes.append( "Invitation" ); +// req.serviceTypes.append( "SocialNetwork" ); +// req.serviceTypes.append( "Space" ); +// req.serviceTypes.append( "Profile" ); + + sendSecureRequest( req.message(), CONTACTS_API ); } @@ -695,20 +634,12 @@ */ void AddressBookService::retrieveGleams() { - QString body( "<ABFindAll xmlns=\"http://www.msn.com/webservices/AddressBook\">\n" - " <abId>00000000-0000-0000-0000-000000000000</abId>\n" - " <abView>Full</abView>\n" - " <deltasOnly>false</deltasOnly>\n" - " <lastChange>0001-01-01T00:00:00.0000000-08:00</lastChange>\n" - " <dynamicItemView>Gleam</dynamicItemView>\n" - " <dynamicItemLastChange>0001-01-01T00:00:00.0000000-08:00</dynamicItemLastChange>\n" - "</ABFindAll>" ); - - sendSecureRequest( new SoapMessage( SERVICE_URL_ADDRESSBOOK, - "http://www.msn.com/webservices/AddressBook/ABFindAll", - createCommonHeader(), - body ), - "Contacts" ); + + ABFindAllRequest req; + req.dynamicItemView = "Gleam"; + req.dynamicItemLastChange = "0001-01-01T00:00:00.0000000-08:00"; + + sendSecureRequest( req.message(), CONTACTS_API ); } @@ -721,230 +652,184 @@ int debugGroupCount = 0; // first thing we have to do is merge the cached data with the deltas. - mergeCacheDeltas( deltaBody ); + mergeABDeltas( deltaBody ); // now we operate on the modified cache data. - QDomElement body = cacheDoc_.documentElement(); + ABFindAllResponse response; + response.parse( addressbookCache_.cacheDocument.documentElement() ); - // Parse the adress book lists - // First parse the one containing the groups - const QDomNodeList& groups( body.elementsByTagName( "Group" ) ); - - for( int index = 0; index < groups.count(); index++ ) + foreach( ABGroup *g, response.groups ) { - const QDomNode& group( groups.item( index ) ); - - const QString& groupId( XmlFunctions::getNodeValue( group, "groupId" ) ); - const QString& name( textNodeDecode( XmlFunctions::getNodeValue( group, "groupInfo/name" ) ) ); - - if( groupId.isEmpty() || name.isEmpty() ) + if ( g->groupId.isEmpty() || g->groupInfo.name.isEmpty() ) { continue; } - - ++debugGroupCount; - emit gotGroup( groupId, name ); + + listsManager_->groupAdd( g->groupId, g->groupInfo.name ); + + debugGroupCount++; } - - // Then parse the list which contains the contacts - const QDomNodeList& contactsNode( body.elementsByTagName( "Contact" ) ); - QList<ABContactInfo> contacts; - QString handle; - QString information; - QStringList guidList; - - for( int index = 0; index < contactsNode.count(); index++ ) + + foreach( ABContact *c, response.contacts ) { - const QDomNode& contact( contactsNode.item( index ) ); - const QDomNode& contactInfo( XmlFunctions::getNode( contact, "contactInfo" ) ); - - // Get some user information - const QString& contactType( XmlFunctions::getNodeValue( contactInfo, "contactType" ) ); - const QString& friendlyName( textNodeDecode( XmlFunctions::getNodeValue( contactInfo, "displayName" ) )); - - // Check if the contact type is Me: it has the information about personal profile - if( contactType == "Me" ) + if ( c->contactInfo.contactType == "Me" ) { - const QString& cid( XmlFunctions::getNodeValue( contactInfo, "CID" ) ); - - // Search for "<annotations>...<Annotation><name>MSN.IM.BLP<name><value>VALUE<value><Annotation>.." - const QDomNodeList& annotations( contactInfo.toElement().elementsByTagName( "Annotation" ) ); - int blp = 0; // = 0 fixes compile time warning - for( int index2 = 0; index2 < annotations.count(); index2++ ) + foreach( ABAnnotation *a, c->contactInfo.annotations ) { - const QDomNode& annotation( annotations.at( index2 ) ); - - if( XmlFunctions::getNodeValue( annotation, "Name" ) == "MSN.IM.BLP" ) + if ( a->name == "MSN.IM.BLP" ) { - // Set BLP argument - blp = XmlFunctions::getNodeValue( annotation, "Value" ).toInt(); + int blp = a->value.toInt(); + emit gotPersonalInformation( QString::number( c->contactInfo.cid ), blp ); break; } } - - emit gotPersonalInformation( cid, blp ); + continue; } - - // If the contact is not in our Messenger contact list, skip it. - if( XmlFunctions::getNodeValue( contactInfo, "isMessengerUser" ) == "false" ) + + QString handle; + + handle = c->contactInfo.passportName; + + // no email from the passportName element. + // try their first contactInfo email if they have one. + if ( handle.isEmpty() ) { - continue; - } + if ( c->contactInfo.emails.count() == 0 ) + { + continue; // skip it - the contact has no email information. + } - ABContactInfo info; + foreach( ABContactEmail *e, c->contactInfo.emails ) + { + if ( e->isMessengerEnabled ) + { + handle = e->email; + break; + } + } + } - // TODO: is this even needed? Can we even get Messenger3 users? - if( XmlFunctions::getNodeValue( contactInfo, "contactEmailType" ) == "Messenger3" ) + if ( handle.isEmpty() ) { - info.isMessenger3 = true; - handle = XmlFunctions::getNodeValue( contactInfo, "email" ).toLower(); - } - else - { - info.isMessenger3 = false; - handle = XmlFunctions::getNodeValue( contactInfo, "passportName" ).toLower(); - } - - // The second condition is an HACK, the handle shouldn't be exist in the list of user - // indeed is impossible to add it on the contact list because microsoft server doesn't accept - // the request. The contact should be removed from the user's list, but for now we prefer to make - // easy the life of the users...for the moment..:P - if( handle.isEmpty() || handle == "mes...@mi..." ) - { - warning() << "Skipped 'mes...@mi...' contact!"; + warning() << "Handle for contact was empty. XML dump:" << c->toString(); continue; } - // Retrieve the other information about the contact - // TODO implement the method for dynamic items, - // please refer to msnpiki in the MSNP13 section. - // and enable retrieval of the other services in - // retrieveMembershipLists(). + + // get the membership. + RoleInfo role = memberships_.take( handle ); + + // and make them a friend (since they're in our AB). + role.memberships |= KMess::MSN_LIST_FRIEND; + + listsManager_->contactAdd( handle, + c->contactInfo.displayName, + role.memberships, + QStringList( c->contactInfo.groupIds ), + c->contactId, + role.network ); - info.handle = handle; - info.friendlyName = friendlyName; - info.contactId = XmlFunctions::getNodeValue( contact, "contactId" ); - info.isMessengerUser = XmlFunctions::getNodeValue( contactInfo, "isMessengerUser" ) == "true"; - info.hasSpace = XmlFunctions::getNodeValue( contactInfo, "hasSpace" ) == "true"; - - // Check if the contact is assigned to any groups - const QDomNodeList& guids( contactInfo.toElement().elementsByTagName( "guid" ) ); - guidList.clear(); - if( ! guids.isEmpty() ) + KMESS_ASSERT( listsManager_->contact( handle ) ); + } + + // now, iterate through the remaining memberships. + // those that are on the Pending list need to be confirmed by the user. + if ( memberships_.count() > 0 ) + { + QHashIterator< QString, RoleInfo > i( memberships_ ); + while ( i.hasNext() ) { - for( int i = 0; i < guids.count(); i++ ) + i.next(); + QString handle = i.key(); + RoleInfo info = i.value(); + + if( ( info.memberships & KMess::MSN_LIST_PENDING ) != 0 ) { - guidList.append( guids.item( i ).toElement().text() ); + listsManager_->contactAdd( info.handle, info.displayName, info.memberships, QStringList(), QString(), info.network ); + emit newPendingContact( listsManager_->contact( info.handle ) ); } - - info.groupList = guidList; } - - contacts.append( info ); } + + debug() << "Address book successfully parsed: found" << response.contacts.count() << "contacts and" << debugGroupCount << "groups."; - debug() << "Address book successfully parsed: found" << contacts.count() << "contacts and" << debugGroupCount << "groups."; - // save it to disk (also throws away cache data from memory) - saveCache(); - + saveCache( addressbookCache_ ); + // Signal that the of address book has been parsed - emit gotAddressBookList( contacts ); + emit addressBookParsed(); } // Parse the membership lists -void AddressBookService::parseMembershipLists( const QDomElement &body ) +void AddressBookService::parseMembershipLists( const QDomElement &deltas ) { - const QDomNodeList& services( body.elementsByTagName( "Service" ) ); - - // New, empty accounts have no Services - if( services.count() == 0 ) + mergeMembershipDeltas( deltas ); + + ABFindMembershipResult response; + response.parse( deltas ); + + if ( response.services.count() == 0 ) { - emit gotMembershipLists( "Messenger", QHash<QString,KMess::MsnMemberships>() ); + retrieveAddressBook(); return; } - - for( int serviceIndex = 0; serviceIndex < services.count(); serviceIndex++ ) + + foreach( ServiceType *service, response.services ) { - const QDomNode& service( services.item( serviceIndex ) ); - - // Get the name of the service - QString serviceType( XmlFunctions::getNodeValue( service, "Info/Handle/Type" ) ); - - if( serviceType.isEmpty() ) + KMess::MsnMemberships memberships; + + foreach( ABMembership *membership, service->memberships ) { - warning() << "Retrieved unknown service type!"; - continue; - } + if ( membership->memberRole == "Allow" ) memberships = KMess::MSN_LIST_ALLOWED; + else if( membership->memberRole == "Block" ) memberships = KMess::MSN_LIST_BLOCKED; + else if( membership->memberRole == "Reverse" ) memberships = KMess::MSN_LIST_REVERSE; + else if( membership->memberRole == "Pending" ) memberships = KMess::MSN_LIST_PENDING; - const QDomNodeList &memberships( XmlFunctions::getNode( service, "Memberships" ).childNodes() ); - - // TODO Parse the timestamp of current retrieve to update the timestamp in the XML list - /* - QDomNodeList listInfo = body.elementsByTagName( "LastChange" ); - QString timestamp( listInfo.item( listInfo.count() - 1 ).toElement().text() ); - */ - - int roleId; - QString role; - QString handle; - - // Parse the service's membership lists and save the results in the contactsRole hash table - QHash<QString,KMess::MsnMemberships> contactsRole; - - for( int index = 0; index < memberships.count(); index++ ) - { - // Parse each role structure - const QDomNode& membership( memberships.item( index ) ); - role = XmlFunctions::getNodeValue( membership, "MemberRole" ); - - roleId = 0; - if( role == "Allow" ) roleId = KMess::MSN_LIST_ALLOWED; - else if( role == "Block" ) roleId = KMess::MSN_LIST_BLOCKED; - else if( role == "Reverse" ) roleId = KMess::MSN_LIST_REVERSE; - else if( role == "Pending" ) roleId = KMess::MSN_LIST_PENDING; - else + foreach( ABBaseMember *member, membership->members ) { - warning() << "Unknown membership role" << role << "in service" << serviceType << "!"; - continue; - } - - // Parse each member - const QDomNodeList& members( membership.toElement().elementsByTagName( "Member" ) ); - for( int i = 0; i < members.count(); i++ ) - { - // FIXME: This only works for the "Messenger" service. Others don't have the PassportName nor Email nodes. - // We need a flexible system! - const QDomNode& memberNode( members.item( i ) ); - handle = XmlFunctions::getNodeValue( memberNode, "PassportName" ).toLower(); - - if( handle.isEmpty() ) + RoleInfo info; + + if( member->type == "Passport" ) { - handle = XmlFunctions::getNodeValue( memberNode, "Email" ).toLower(); - - if( handle.isEmpty() ) - { - continue; - } + ABPassportMember *p = (ABPassportMember *)member; + info.handle = p->passportName; + info.network = KMess::NetworkMsn; } - // HACK: See parseAddressBook() for details - else if( handle == "mes...@mi..." ) + else if ( member->type == "Email" ) { - warning() << "Skipped 'mes...@mi...' contact!"; + ABEmailMember *e = (ABEmailMember *)member; + info.handle = e->email; + info.network = KMess::NetworkYahoo; // for now. + } + else + { + warning() << "Unhandled member type:" << member->type; continue; } + + if ( info.handle == "mes...@mi..." ) + { + continue; // skip? + } + + if ( memberships_.contains( info.handle ) ) + { + info.memberships = memberships_.value( info.handle ).memberships; + } + + info.memberships |= memberships; - // Insert the current contact in the hash with their respective role - // If the contact is already listed, the new value is OR'ed (set bit flag) with the old roleId - contactsRole.insert( handle, contactsRole.value( handle, 0 ) | (KMess::MsnMemberships)roleId ); + memberships_.insert( info.handle, info ); } } - - // When finished parsing the service's memberships list, signal that its list is ready - emit gotMembershipLists( serviceType, contactsRole ); } + + saveCache( membershipCache_ ); + + // now fetch the address book. + retrieveAddressBook(); } @@ -1015,17 +900,18 @@ { debug() << "Sync timestamp was invalid; doing full AB request."; + #warning InvalidSyncTimestamp - need to identify whether to request Memberships or AB again // wipe the cache file. - if ( ! QFile( cacheFile_ ).remove() ) + if ( ! QFile( addressbookCache_.cacheFile ).remove() ) { - warning() << "Unable to remove cache file " << cacheFile_ << "; caching will be ignored until this is resolved."; - cacheFile_ = QString(); + warning() << "Unable to remove cache file " << addressbookCache_.cacheFile << "; caching will be ignored until this is resolved."; + addressbookCache_.cacheFile = QString(); } - cacheDoc_ = QDomDocument(); - cacheTimestamp_ = QDateTime(); + addressbookCache_.cacheDocument = QDomDocument(); + addressbookCache_.cacheTimestamp = QDateTime(); - retrieveAddressBook( cacheFile_ ); + retrieveAddressBook(); } else { @@ -1059,12 +945,12 @@ if( ! body.firstChildElement( "FindMembershipResponse" ).isNull() ) { - parseMembershipLists( body ); + parseMembershipLists( body.firstChildElement( "FindMembershipResponse" ).firstChildElement( "FindMembershipResult" ) ); return; } else if( ! body.firstChildElement( "ABFindAllResponse" ).isNull() ) { - parseAddressBook( body ); + parseAddressBook( body.firstChildElement( "ABFindAllResponse" ).firstChildElement( "ABFindAllResult" ) ); return; } else if( ! body.firstChildElement( "ABContactUpdateResponse" ).isNull() ) @@ -1200,26 +1086,26 @@ // Save cache to a file. // Call this after you're done with cacheDoc_, because it will be thrown away after this // method finishes. -void AddressBookService::saveCache() +void AddressBookService::saveCache( ABCacheData &data ) { - QFile cache( cacheFile_ ); + QFile cache( data.cacheFile ); if ( ! cache.open( QIODevice::WriteOnly ) ) { - warning() << "Cannot open the cache file (" << cacheFile_ << ") for writing; address book caching will not occur."; + warning() << "Cannot open the cache file (" << data.cacheFile << ") for writing; address book caching will not occur."; return; } // put the last save information in the file... // tell QDomDocument to dump itself to the cache file... QTextStream stream( &cache ); - cacheDoc_.save( stream, 2 ); + data.cacheDocument.save( stream, 2 ); cache.close(); - debug() << "Wrote cache to" << cacheFile_; + debug() << "Wrote cache to" << data.cacheFile; // remove... [truncated message content] |