void ActiveSyncSource::readItem(const std::string &luid, std::string &item) { // return straight from cache? std::map<std::string, std::string>::iterator it = m_items.find(luid); if (it == m_items.end()) { // no, must fetch EASItemPtr tmp(eas_item_info_new(), "EasItem"); GErrorCXX gerror; if (!eas_sync_handler_fetch_item(m_handler, m_folder.c_str(), luid.c_str(), tmp, getEasType(), gerror)) { if (gerror.m_gerror->message && strstr(gerror.m_gerror->message, "ObjectNotFound") /* gerror.matches(EAS_CONNECTION_ERROR, EAS_CONNECTION_ITEMOPERATIONS_ERROR_OBJECTNOTFOUND) (gdb) p *m_gerror $7 = {domain = 156, code = 36, message = 0xda2940 "GDBus.Error:org.meego.activesyncd.ItemOperationsError.ObjectNotFound: Document library - The object was not found or access denied."} */) { throwError(STATUS_NOT_FOUND, "item not found: " + luid); } else { gerror.throwError(StringPrintf("reading eas item %s", luid.c_str())); } } if (!tmp->data) { throwError(StringPrintf("no body returned for eas item %s", luid.c_str())); } item = tmp->data; } else { item = it->second; } }
boost::shared_ptr<AuthProvider> createSignonAuthProvider(const InitStateString &username, const InitStateString &password) { // Expected content of parameter GVariant. boost::shared_ptr<GVariantType> hashtype(g_variant_type_new("a{sv}"), g_variant_type_free); // 'username' is the part after signon: which we can parse directly. GErrorCXX gerror; GVariantCXX parametersVar(g_variant_parse(hashtype.get(), username.c_str(), NULL, NULL, gerror), TRANSFER_REF); if (!parametersVar) { gerror.throwError(SE_HERE, "parsing 'signon:' username"); } GHashTableCXX parameters(Variant2HashTable(parametersVar)); // Extract the values that we expect in the parameters hash. guint32 signonID; const char *method; const char *mechanism; GVariant *value; value = (GVariant *)g_hash_table_lookup(parameters, "identity"); if (!value || !g_variant_type_equal(G_VARIANT_TYPE_UINT32, g_variant_get_type(value))) { SE_THROW("need 'identity: <numeric ID>' in 'signon:' parameters"); } signonID = g_variant_get_uint32(value); value = (GVariant *)g_hash_table_lookup(parameters, "method"); if (!value || !g_variant_type_equal(G_VARIANT_TYPE_STRING, g_variant_get_type(value))) { SE_THROW("need 'method: <string>' in 'signon:' parameters"); } method = g_variant_get_string(value, NULL); value = (GVariant *)g_hash_table_lookup(parameters, "mechanism"); if (!value || !g_variant_type_equal(G_VARIANT_TYPE_STRING, g_variant_get_type(value))) { SE_THROW("need 'mechanism: <string>' in 'signon:' parameters"); } mechanism = g_variant_get_string(value, NULL); value = (GVariant *)g_hash_table_lookup(parameters, "session"); if (!value || !g_variant_type_equal(hashtype.get(), g_variant_get_type(value))) { SE_THROW("need 'session: <hash>' in 'signon:' parameters"); } GHashTableCXX sessionData(Variant2HashTable(value)); SE_LOG_DEBUG(NULL, "using identity %u, method %s, mechanism %s", signonID, method, mechanism); SignonIdentityCXX identity(signon_identity_new_from_db(signonID), TRANSFER_REF); SE_LOG_DEBUG(NULL, "using signond identity %d", signonID); SignonAuthSessionCXX authSession(signon_identity_create_session(identity, method, gerror), TRANSFER_REF); boost::shared_ptr<AuthProvider> provider(new SignonAuthProvider(authSession, sessionData, mechanism)); return provider; }
GLibNotify::GLibNotify(const char *file, const callback_t &callback) : m_callback(callback) { GFileCXX filecxx(g_file_new_for_path(file), TRANSFER_REF); GErrorCXX gerror; GFileMonitorCXX monitor(g_file_monitor_file(filecxx.get(), G_FILE_MONITOR_NONE, NULL, gerror), TRANSFER_REF); m_monitor.swap(monitor); if (!m_monitor) { gerror.throwError(SE_HERE, std::string("monitoring ") + file); } g_signal_connect_after(m_monitor.get(), "changed", G_CALLBACK(changed), (void *)&m_callback); }
void ActiveSyncSource::deleteItem(const string &luid) { // asking to delete a non-existent item via ActiveSync does not // trigger an error; this is expected by the caller, so detect // the problem by looking up the item in our list (and keep the // list up-to-date elsewhere) if (m_ids && m_ids->readProperty(luid).empty()) { throwError(STATUS_NOT_FOUND, "item not found: " + luid); } // send delete request // TODO (?): batch delete requests GListCXX<char, GSList> items; items.push_back((char *)luid.c_str()); GErrorCXX gerror; char *buffer; if (!eas_sync_handler_delete_items(m_handler, m_currentSyncKey.c_str(), &buffer, getEasType(), m_folder.c_str(), items, gerror)) { gerror.throwError("deleting eas item"); } GStringPtr bufferOwner(buffer, "delete items: empty sync key returned"); // remove from item list if (m_ids) { m_items.erase(luid); m_ids->removeProperty(luid); } // update key m_currentSyncKey = buffer; }
void ForkExecParent::start() { if (m_watchChild) { SE_THROW("child already started"); } // boost::shared_ptr<ForkExecParent> me = ...; GDBusCXX::DBusErrorCXX dbusError; SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: preparing for child process %s", m_helper.c_str()); m_server = GDBusCXX::DBusServerCXX::listen("", &dbusError); if (!m_server) { dbusError.throwFailure("starting server"); } m_server->setNewConnectionCallback(boost::bind(&ForkExecParent::newClientConnection, this, _2)); // look for helper binary std::string helper; GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD; if (m_helper.find('/') == m_helper.npos) { helper = getEnv("SYNCEVOLUTION_LIBEXEC_DIR", ""); if (helper.empty()) { // env variable not set, look in libexec dir helper = SYNCEVO_LIBEXEC; helper += "/"; helper += m_helper; if (access(helper.c_str(), R_OK)) { // some error, try PATH flags = (GSpawnFlags)(flags | G_SPAWN_SEARCH_PATH); helper = m_helper; } } else { // use env variable without further checks, must work helper += "/"; helper += m_helper; } } else { // absolute path, use it helper = m_helper; } m_argvStrings.push_back(helper); m_argv.reset(AllocStringArray(m_argvStrings)); for (char **env = environ; *env; env++) { if (!boost::starts_with(*env, ForkExecEnvVar)) { m_envStrings.push_back(*env); } } // pass D-Bus address via env variable m_envStrings.push_back(ForkExecEnvVar + m_server->getAddress()); m_env.reset(AllocStringArray(m_envStrings)); SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: running %s with D-Bus address %s", helper.c_str(), m_server->getAddress().c_str()); // Check which kind of output redirection is wanted. m_mergedStdoutStderr = !m_onOutput.empty(); if (!m_onOutput.empty()) { m_mergedStdoutStderr = true; } GErrorCXX gerror; int err = -1, out = -1; if (!g_spawn_async_with_pipes(NULL, // working directory static_cast<gchar **>(m_argv.get()), static_cast<gchar **>(m_env.get()), flags, // child setup function: redirect stdout to stderr m_mergedStdoutStderr ? setStdoutToStderr : NULL, NULL, // child setup user data &m_childPid, NULL, // set stdin to /dev/null (m_mergedStdoutStderr || m_onStdout.empty()) ? NULL : &out, (m_mergedStdoutStderr || !m_onStderr.empty()) ? &err : NULL, gerror)) { m_childPid = 0; gerror.throwError("spawning child"); } // set up output redirection, ignoring failures setupPipe(m_err, m_errID, err); setupPipe(m_out, m_outID, out); SE_LOG_DEBUG(NULL, NULL, "ForkExecParent: child process for %s has pid %ld", helper.c_str(), (long)m_childPid); // TODO: introduce C++ wrapper around GSource m_watchChild = g_child_watch_source_new(m_childPid); g_source_set_callback(m_watchChild, (GSourceFunc)watchChildCallback, this, NULL); g_source_attach(m_watchChild, NULL); }
void ActiveSyncSource::beginSync(const std::string &lastToken, const std::string &resumeToken) { // erase content which might have been set in a previous call reset(); // claim item node for ids, if not done yet if (m_itemNode && !m_ids) { m_ids.swap(m_itemNode); } // incremental sync (non-empty token) or start from scratch m_startSyncKey = lastToken; if (lastToken.empty()) { // slow sync: wipe out cached list of IDs, will be filled anew below SE_LOG_DEBUG(this, NULL, "sync key empty, starting slow sync"); m_ids->clear(); } else { SE_LOG_DEBUG(this, NULL, "sync key %s for account '%s' folder '%s', starting incremental sync", lastToken.c_str(), m_account.c_str(), m_folder.c_str()); } gboolean moreAvailable = TRUE; m_currentSyncKey = m_startSyncKey; // same logic as in ActiveSyncCalendarSource::beginSync() bool slowSync = false; for (bool firstIteration = true; moreAvailable; firstIteration = false) { gchar *buffer = NULL; GErrorCXX gerror; EASItemsCXX created, updated; EASIdsCXX deleted; bool wasSlowSync = m_currentSyncKey.empty(); if (!eas_sync_handler_get_items(m_handler, m_currentSyncKey.c_str(), &buffer, getEasType(), m_folder.c_str(), created, updated, deleted, &moreAvailable, gerror)) { if (gerror.m_gerror && /* gerror.m_gerror->domain == EAS_TYPE_CONNECTION_ERROR && gerror.m_gerror->code == EAS_CONNECTION_SYNC_ERROR_INVALIDSYNCKEY && */ gerror.m_gerror->message && strstr(gerror.m_gerror->message, "Sync error: Invalid synchronization key") && firstIteration) { // fall back to slow sync slowSync = true; m_currentSyncKey = ""; m_ids->clear(); continue; } gerror.throwError("reading ActiveSync changes"); } GStringPtr bufferOwner(buffer, "reading changes: empty sync key returned"); // TODO: Test that we really get an empty token here for an unexpected slow // sync. If not, we'll start an incremental sync here and later the engine // will ask us for older, unmodified item content which we won't have. // populate ID lists and content cache BOOST_FOREACH(EasItemInfo *item, created) { if (!item->server_id) { throwError("no server ID for new eas item"); } string luid(item->server_id); if (luid.empty()) { throwError("empty server ID for new eas item"); } SE_LOG_DEBUG(this, NULL, "new item %s", luid.c_str()); addItem(luid, NEW); m_ids->setProperty(luid, "1"); if (!item->data) { throwError(StringPrintf("no body returned for new eas item %s", luid.c_str())); } m_items[luid] = item->data; } BOOST_FOREACH(EasItemInfo *item, updated) { if (!item->server_id) { throwError("no server ID for updated eas item"); } string luid(item->server_id); if (luid.empty()) { throwError("empty server ID for updated eas item"); } SE_LOG_DEBUG(this, NULL, "updated item %s", luid.c_str()); addItem(luid, UPDATED); // m_ids.setProperty(luid, "1"); not necessary, should already exist (TODO: check?!) if (!item->data) { throwError(StringPrintf("no body returned for updated eas item %s", luid.c_str())); } m_items[luid] = item->data; } BOOST_FOREACH(const char *serverID, deleted) { if (!serverID) { throwError("no server ID for deleted eas item"); } string luid(serverID); if (luid.empty()) { throwError("empty server ID for deleted eas item"); } SE_LOG_DEBUG(this, NULL, "deleted item %s", luid.c_str()); addItem(luid, DELETED); m_ids->removeProperty(luid); } // update key m_currentSyncKey = buffer; // Google hack: if we started with an empty sync key (= slow sync) // and got no results (= existing items), then try one more time, // because Google only seems to report results when asked with // a valid sync key. As an additional sanity check make sure that // we have a valid sync key now. if (wasSlowSync && created.empty() && !m_currentSyncKey.empty()) { moreAvailable = true; } } // now also generate full list of all current items: // old items + new (added to m_ids above) - deleted (removed above) ConfigProps props; m_ids->readProperties(props); BOOST_FOREACH(const StringPair &entry, props) { const std::string &luid = entry.first; SE_LOG_DEBUG(this, NULL, "existing item %s", luid.c_str()); addItem(luid, ANY); } if (slowSync) { // tell engine that we need a slow sync, if it didn't know already SE_THROW_EXCEPTION_STATUS(StatusException, "ActiveSync error: Invalid synchronization key", STATUS_SLOW_SYNC_508); } }
SyncSourceSerialize::InsertItemResult ActiveSyncSource::insertItem(const std::string &luid, const std::string &data) { SyncSourceSerialize::InsertItemResult res; EASItemPtr tmp(eas_item_info_new(), "EasItem"); EasItemInfo *item = tmp.get(); if (!luid.empty()) { // update item->server_id = g_strdup(luid.c_str()); } else { // add // TODO: is a local id needed? We don't have one. } item->data = g_strdup(data.c_str()); EASItemsCXX items; items.push_front(tmp.release()); GErrorCXX gerror; char *buffer; // distinguish between update (existing luid) // or creation (empty luid) if (luid.empty()) { // send item to server if (!eas_sync_handler_add_items(m_handler, m_currentSyncKey.c_str(), &buffer, getEasType(), m_folder.c_str(), items, gerror)) { gerror.throwError("adding eas item"); } if (!item->server_id) { throwError("no server ID for new eas item"); } // get new ID from updated item res.m_luid = item->server_id; if (res.m_luid.empty()) { throwError("empty server ID for new eas item"); } // TODO: if someone else has inserted a new calendar item // with the same UID as the one we are trying to insert here, // what will happen? Does the ActiveSync server prevent // adding our own version of the item or does it merge? // res.m_merged = ??? } else { // update item on server if (!eas_sync_handler_update_items(m_handler, m_currentSyncKey.c_str(), &buffer, getEasType(), m_folder.c_str(), items, gerror)) { gerror.throwError("updating eas item"); } res.m_luid = luid; } GStringPtr bufferOwner(buffer, "insert item: empty sync key returned"); // add/update in cache if (m_ids) { m_items[res.m_luid] = data; m_ids->setProperty(res.m_luid, "1"); } // update key m_currentSyncKey = buffer; return res; }