This patch demonstrates how switching from the current, synchronous to the corresponding asynchronous API could work. This helps to avoid some timeouts with EDS-DBus, but not all: the e_book_async_get_changes() call still times out. Index: src/EvolutionContactSource.cpp =================================================================== RCS file: /cvsroot/sync4jevolution/sync4jevolution/src/EvolutionContactSource.cpp,v retrieving revision 1.50 diff -c -r1.50 EvolutionContactSource.cpp *** src/EvolutionContactSource.cpp 3 Jan 2007 20:58:41 -0000 1.50 --- src/EvolutionContactSource.cpp 22 Feb 2007 19:27:50 -0000 *************** *** 27,32 **** --- 27,33 ---- #ifdef ENABLE_EBOOK #include "EvolutionContactSource.h" + #include #include #include "vocl/VConverter.h" *************** *** 154,159 **** --- 155,207 ---- } } + void EvolutionContactSource::addContacts(void *custom, GList *nextItem) + { + EvolutionContactSource *source = (EvolutionContactSource *)custom; + + while (nextItem) { + const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data), + E_CONTACT_UID); + source->m_allItems.addItem(uid); + nextItem = nextItem->next; + } + } + + void EvolutionContactSource::addChanges(EBook *book, + EBookStatus status, + GList *nextItem, + gpointer custom) + { + EvolutionContactSource *source = (EvolutionContactSource *)custom; + source->m_status = status; + + while (nextItem) { + EBookChange *ebc = (EBookChange *)nextItem->data; + + if (ebc->contact) { + const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID ); + + if (uid) { + switch (ebc->change_type) { + case E_BOOK_CHANGE_CARD_ADDED: + source->m_newItems.addItem(uid); + break; + case E_BOOK_CHANGE_CARD_MODIFIED: + source->m_updatedItems.addItem(uid); + break; + case E_BOOK_CHANGE_CARD_DELETED: + source->m_deletedItems.addItem(uid); + break; + } + } + } + nextItem = nextItem->next; + } + + source->m_loop.quit(); + } + + void EvolutionContactSource::beginSyncThrow(bool needAll, bool needPartial, bool deleteLocal) *************** *** 161,234 **** GError *gerror = NULL; if (deleteLocal) { ! eptr allItemsQuery( e_book_query_any_field_contains(""), "query" ); ! GList *nextItem; ! if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) { ! throwError( "reading all items", gerror ); ! } ! while (nextItem) { ! const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data), ! E_CONTACT_UID); ! if (!e_book_remove_contact( m_addressbook, uid, &gerror ) ) { ! throwError( string( "deleting contact " ) + uid, gerror ); } - nextItem = nextItem->next; } } if (needAll) { ! eptr allItemsQuery( e_book_query_any_field_contains(""), "query" ); ! GList *nextItem; ! if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) { ! throwError( "reading all items", gerror ); ! } ! while (nextItem) { ! const char *uid = (const char *)e_contact_get_const(E_CONTACT(nextItem->data), ! E_CONTACT_UID); ! m_allItems.addItem(uid); ! nextItem = nextItem->next; ! } } if (needPartial) { GList *nextItem; if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) { throwError( "reading changes", gerror ); } ! while (nextItem) { ! EBookChange *ebc = (EBookChange *)nextItem->data; ! ! if (ebc->contact) { ! const char *uid = (const char *)e_contact_get_const( ebc->contact, E_CONTACT_UID ); ! ! if (uid) { ! switch (ebc->change_type) { ! case E_BOOK_CHANGE_CARD_ADDED: ! m_newItems.addItem(uid); ! break; ! case E_BOOK_CHANGE_CARD_MODIFIED: ! m_updatedItems.addItem(uid); ! break; ! case E_BOOK_CHANGE_CARD_DELETED: ! m_deletedItems.addItem(uid); ! break; ! } ! } ! } ! nextItem = nextItem->next; } } } void EvolutionContactSource::endSyncThrow() { if (m_isModified) { - GError *gerror = NULL; - GList *nextItem; // move change_id forward so that our own changes are not listed the next time ! if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) { ! throwError( "reading changes", gerror ); } } resetItems(); --- 209,264 ---- GError *gerror = NULL; if (deleteLocal) { ! m_allItems.clear(); ! listAllContacts(addContacts, this); ! ! EvolutionSyncSource::itemList::const_iterator it; ! for (it = m_allItems.begin(); ! it != m_allItems.end(); ! ++it) { ! if (!e_book_remove_contact( m_addressbook, it->c_str(), &gerror ) ) { ! throwError( string( "deleting contact " ) + *it, gerror ); } } + m_allItems.clear(); } if (needAll) { ! listAllContacts(addContacts, this); } if (needPartial) { + #if 0 + // times out on N770 + GError *gerror = NULL; GList *nextItem; if (!e_book_get_changes( m_addressbook, (char *)m_changeId.c_str(), &nextItem, &gerror )) { throwError( "reading changes", gerror ); } ! addChanges(m_addressbook, E_BOOK_ERROR_OK, nextItem, (gpointer)this); ! #else ! if (e_book_async_get_changes(m_addressbook, (char *)m_changeId.c_str(), addChanges, this)) { ! throwError( "reading changes", gerror ); } + m_loop.run(); + if (m_status != E_BOOK_ERROR_OK) { + throw runtime_error("reading changes stopped with an error"); + } + #endif } } void EvolutionContactSource::endSyncThrow() { if (m_isModified) { // move change_id forward so that our own changes are not listed the next time ! if (e_book_async_get_changes(m_addressbook, (char *)m_changeId.c_str(), addChanges, this)) { ! throw runtime_error("reading changes"); ! } ! m_loop.run(); ! if (m_status != E_BOOK_ERROR_OK) { ! throw runtime_error("reading changes stopped with an error"); } } resetItems(); *************** *** 241,263 **** m_addressbook = NULL; } ! void EvolutionContactSource::exportData(ostream &out) { ! eptr allItemsQuery( e_book_query_any_field_contains(""), "query" ); ! GList *nextItem; ! GError *gerror = NULL; ! if (!e_book_get_contacts( m_addressbook, allItemsQuery, &nextItem, &gerror )) { ! throwError( "reading all items", gerror ); ! } while (nextItem) { eptr vcardstr(e_vcard_to_string(&E_CONTACT(nextItem->data)->parent, EVC_FORMAT_VCARD_30)); ! out << (const char *)vcardstr << "\r\n\r\n"; nextItem = nextItem->next; } } SyncItem *EvolutionContactSource::createItem( const string &uid, SyncState state ) { logItem( uid, "extracting from EV" ); --- 271,296 ---- m_addressbook = NULL; } ! ! static void dumpContacts(void *custom, GList *nextItem) { ! ostream *out = (ostream *)custom; ! while (nextItem) { eptr vcardstr(e_vcard_to_string(&E_CONTACT(nextItem->data)->parent, EVC_FORMAT_VCARD_30)); ! *out << (const char *)vcardstr << "\r\n\r\n"; ! nextItem = nextItem->next; } } + void EvolutionContactSource::exportData(ostream &out) + { + listAllContacts(dumpContacts, (void *)&out); + } + SyncItem *EvolutionContactSource::createItem( const string &uid, SyncState state ) { logItem( uid, "extracting from EV" ); *************** *** 773,776 **** --- 806,876 ---- } } + class EvolutionContactListAll { + public: + EvolutionContactListAll(void (*processList)(void *custom, GList *list), void *custom, EvolutionContactSource &source): + m_processList(processList), + m_custom(custom), + m_source(source), + m_status(E_BOOK_VIEW_STATUS_OK) + {} + + static void contactsAdded(EBookView *ebookview, + gpointer arg1, + gpointer user_data) { + EvolutionContactListAll *listAll = (EvolutionContactListAll *)user_data; + listAll->m_processList(listAll->m_custom, (GList *)arg1); + } + + static void sequenceDone(EBookView *ebookview, + gint arg1, + gpointer user_data) { + EvolutionContactListAll *listAll = (EvolutionContactListAll *)user_data; + listAll->m_status = (EBookViewStatus)arg1; + listAll->m_source.m_loop.quit(); + } + + /** throw an error exception if an error occurred */ + void checkStatus() { + if (m_status != E_BOOK_VIEW_STATUS_OK) { + throw runtime_error("iterating over all contacts failed"); + } + } + + private: + void (*m_processList)(void *custom, GList *list); + void *m_custom; + EvolutionContactSource &m_source; + EBookViewStatus m_status; + }; + + + + + + void EvolutionContactSource::listAllContacts(void (*processList)(void *custom, GList *list), void *custom) + { + eptr allItemsQuery( e_book_query_any_field_contains(""), "query" ); + GError *gerror = NULL; + EBookView *viewptr; + + if (e_book_get_book_view(m_addressbook, allItemsQuery, NULL, -1, &viewptr, &gerror)) { + eptr view(viewptr); + EvolutionContactListAll listAll(processList, custom, *this); + + g_signal_connect(viewptr, "contacts-added", G_CALLBACK(listAll.contactsAdded), &listAll); + g_signal_connect(viewptr, "sequence-complete", G_CALLBACK(listAll.sequenceDone), &listAll); + e_book_view_start(view); + m_loop.run(); + e_book_view_stop(view); + // workaround for http://bugzilla.gnome.org/show_bug.cgi?id=399011 + // Without the sleep() EDS often (but not always) crashes in e_book_backend_get_book_views() + // during one of the following calls. + sleep(1); + listAll.checkStatus(); + } else { + throwError( "getting view on addressbook", gerror ); + } + } + #endif /* ENABLE_EBOOK */ Index: src/EvolutionContactSource.h =================================================================== RCS file: /cvsroot/sync4jevolution/sync4jevolution/src/EvolutionContactSource.h,v retrieving revision 1.22 diff -c -r1.22 EvolutionContactSource.h *** src/EvolutionContactSource.h 10 Dec 2006 17:35:18 -0000 1.22 --- src/EvolutionContactSource.h 22 Feb 2007 19:27:50 -0000 *************** *** 32,37 **** --- 32,49 ---- #include /** + * callback used by EvolutionContactSource::listAll() and + */ + class EvolutionCallback + { + public: + /** + * Called to iterate over data. Content of list depends on context. + */ + virtual void processList(GList *list) = 0; + }; + + /** * Implements access to Evolution address books. */ class EvolutionContactSource : public EvolutionSyncSource *************** *** 81,87 **** // implementation of SyncSource // virtual ArrayElement *clone() { return new EvolutionContactSource(*this); } ! protected: // // implementation of EvolutionSyncSource callbacks --- 93,102 ---- // implementation of SyncSource // virtual ArrayElement *clone() { return new EvolutionContactSource(*this); } ! ! /** start and stop event processing on this source */ ! EvolutionAsync m_loop; ! protected: // // implementation of EvolutionSyncSource callbacks *************** *** 142,149 **** --- 157,191 ---- insert("CALURI"); } } m_uniqueProperties; + + /** + * extracts all contacts + * + * @param processList is fed the contacts, possibly in multiple chunks + * @param custom pointer passed through to processList + */ + void listAllContacts(void (*processList)(void *custom, GList *list), void *custom); + + /** + * callback for listAllContacts() which adds all contacts to m_allItems + */ + static void addContacts(void *custom, GList *nextItem); + + /** + * EBookListCallback for beginSyncThrow()'s e_book_async_get_changes () + */ + static void addChanges(EBook *book, + EBookStatus status, + GList *nextItem, + gpointer custom); + /** + * status passed to addChanges() + */ + EBookStatus m_status; }; + + #endif // ENABLE_EBOOK #endif // INCL_EVOLUTIONCONTACTSOURCE Index: src/EvolutionSmartPtr.h =================================================================== RCS file: /cvsroot/sync4jevolution/sync4jevolution/src/EvolutionSmartPtr.h,v retrieving revision 1.8 diff -c -r1.8 EvolutionSmartPtr.h *** src/EvolutionSmartPtr.h 10 Dec 2006 17:35:19 -0000 1.8 --- src/EvolutionSmartPtr.h 22 Feb 2007 19:27:50 -0000 *************** *** 34,39 **** --- 34,40 ---- void inline unref( char *pointer ) { free( pointer ); } void inline unref( GObject *pointer ) { g_object_unref( pointer ); } + void inline unref( GMainLoop *pointer ) { g_main_loop_unref( pointer ); } #ifdef ENABLE_EBOOK void inline unref( EBookQuery *pointer ) { e_book_query_unref( pointer ); } #endif Index: src/EvolutionSyncClient.cpp =================================================================== RCS file: /cvsroot/sync4jevolution/sync4jevolution/src/EvolutionSyncClient.cpp,v retrieving revision 1.24 diff -c -r1.24 EvolutionSyncClient.cpp *** src/EvolutionSyncClient.cpp 17 Dec 2006 16:33:45 -0000 1.24 --- src/EvolutionSyncClient.cpp 22 Feb 2007 19:27:50 -0000 *************** *** 432,438 **** int res = DMTClientConfig::readDevInfoConfig(syncMLNode, syncMLNode); // always read device ID from the traditional property "deviceId" ! eptr tmp(syncMLNode.readPropertyValue("deviceId")); deviceConfig.setDevID(tmp); return res; --- 432,438 ---- int res = DMTClientConfig::readDevInfoConfig(syncMLNode, syncMLNode); // always read device ID from the traditional property "deviceId" ! arrayptr tmp(syncMLNode.readPropertyValue("deviceId")); deviceConfig.setDevID(tmp); return res; *************** *** 482,489 **** // redirect logging as soon as possible SourceList sourceList(m_server, m_doLogging); ! eptr logdir(config.getSyncMLNode()->readPropertyValue("logdir")); ! eptr maxlogdirs(config.getSyncMLNode()->readPropertyValue("maxlogdirs")); sourceList.setLogdir(logdir, atoi(maxlogdirs)); SyncSourceConfig *sourceconfigs = config.getSyncSourceConfigs(); --- 482,489 ---- // redirect logging as soon as possible SourceList sourceList(m_server, m_doLogging); ! arrayptr logdir(config.getSyncMLNode()->readPropertyValue("logdir")); ! arrayptr maxlogdirs(config.getSyncMLNode()->readPropertyValue("maxlogdirs")); sourceList.setLogdir(logdir, atoi(maxlogdirs)); SyncSourceConfig *sourceconfigs = config.getSyncSourceConfigs(); Index: src/EvolutionSyncSource.h =================================================================== RCS file: /cvsroot/sync4jevolution/sync4jevolution/src/EvolutionSyncSource.h,v retrieving revision 1.24 diff -c -r1.24 EvolutionSyncSource.h *** src/EvolutionSyncSource.h 10 Dec 2006 17:35:19 -0000 1.24 --- src/EvolutionSyncSource.h 22 Feb 2007 19:27:51 -0000 *************** *** 33,38 **** --- 33,40 ---- #include #include + #include + /** * This class implements the functionality shared by * both EvolutionCalenderSource and EvolutionContactSource: *************** *** 371,374 **** --- 373,400 ---- string m_user, m_passwd; }; + /** + * Utility class which hides the mechanisms needed to handle events + * during asynchronous calls. + */ + class EvolutionAsync { + public: + EvolutionAsync() : + m_loop(g_main_loop_new(NULL, FALSE), "main loop") + {} + + /** start processing events */ + void run() { + g_main_loop_run(m_loop); + } + + /** stop processing events, to be called inside run() by callback */ + void quit() { + g_main_loop_quit(m_loop); + } + + private: + eptr m_loop; + }; + #endif // INCL_EVOLUTIONSYNCSOURCE