2 * Copyright (C) 2005-2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 #include "EvolutionSyncSource.h"
22 #include <syncevo/SmartPtr.h>
23 #include <syncevo/SyncContext.h>
24 #include <syncevo/GLibSupport.h>
27 #include <syncevo/GValueSupport.h>
28 SE_GLIB_TYPE(GKeyFile, g_key_file)
31 #include <syncevo/declarations.h>
38 void EvolutionSyncSource::getDatabasesFromRegistry(SyncSource::Databases &result,
39 const char *extension,
40 ESource *(*refDef)(ESourceRegistry *))
42 ESourceRegistryCXX registry = EDSRegistryLoader::getESourceRegistry();
43 ESourceListCXX sources(e_source_registry_list_sources(registry, extension));
44 ESourceCXX def(refDef ? refDef(registry) : NULL,
46 BOOST_FOREACH (ESource *source, sources) {
47 result.push_back(Database(e_source_get_display_name(source),
48 e_source_get_uid(source),
49 e_source_equal(def, source)));
53 static void handleErrorCB(EClient */*client*/, const gchar *error_msg, gpointer user_data)
55 EvolutionSyncSource *that = static_cast<EvolutionSyncSource *>(user_data);
56 SE_LOG_ERROR(that->getDisplayName(), "%s", error_msg);
59 EClientCXX EvolutionSyncSource::openESource(const char *extension,
60 ESource *(*refBuiltin)(ESourceRegistry *),
61 const boost::function<EClient *(ESource *, GError **gerror)> &newClient)
65 ESourceRegistryCXX registry = EDSRegistryLoader::getESourceRegistry();
66 ESourceListCXX sources(e_source_registry_list_sources(registry, extension));
67 string id = getDatabaseID();
68 ESource *source = findSource(sources, id);
72 if (refBuiltin && (id.empty() || id == "<<system>>")) {
73 ESourceCXX builtin(refBuiltin(registry), TRANSFER_REF);
74 client = EClientCXX::steal(newClient(builtin, gerror));
75 // } else if (!id.compare(0, 7, "file://")) {
76 // TODO: create source
77 // m_calendar = ECalClientCXX::steal(e_cal_client_new_from_uri(id.c_str(), sourceType(), gerror));
79 throwError(string("database not found: '") + id + "'");
83 client = EClientCXX::steal(newClient(source, gerror));
87 throwError("accessing database", gerror);
91 g_signal_connect (client, "backend-error", G_CALLBACK(handleErrorCB), this);
92 g_signal_connect_after(client,
94 G_CALLBACK(SyncContext::fatalError),
95 (void *)"Evolution Data Server has died unexpectedly.");
99 // Always allow EDS to create the database. "only-if-exists =
100 // true" does not make sense.
101 if (!e_client_open_sync(client, false, NULL, gerror)) {
102 if (gerror && g_error_matches(gerror, E_CLIENT_ERROR, E_CLIENT_ERROR_BUSY)) {
105 } else if (created) {
106 // Opening newly created address books often failed in
107 // old EDS releases - try again. Probably covered by
108 // more recently added E_CLIENT_ERROR_BUSY check above.
112 throwError("opening database", gerror);
120 // record result for SyncSource::getDatabase()
121 source = e_client_get_source(client);
123 Database database(e_source_get_display_name(source),
124 e_source_get_uid(source));
125 setDatabase(database);
131 SyncSource::Database EvolutionSyncSource::createDatabase(const Database &database)
133 // We'll need this later. Create it before doing any real work.
134 ESourceRegistryCXX registry = EDSRegistryLoader::getESourceRegistry();
136 // Clone the system DB. This allows the distro to change the
137 // configuration (backend, extensions (= in particular
138 // the contacts DB summary fields)) without having to
139 // modify SyncEvolution.
140 ESourceCXX systemSource = refSystemDB();
142 PlainGStr ini(e_source_to_string(systemSource, &len));
144 // Modify the entries in the key file directly. We can't
145 // instantiate an ESource (no API for it), copying the values from
146 // the key file into a fresh ESource is difficult (would have to
147 // reimplement EDS internal encoding/decoding), and copying from
148 // systemSource is hard (don't know which extensions it has,
149 // cannot instantiate extensions of unknown types, because
150 // e_source_get_extension() only works for types that were
152 static const char *mainSection = "Data Source";
153 GKeyFileCXX keyfile(g_key_file_new(), TRANSFER_REF);
155 if (!g_key_file_load_from_data(keyfile, ini, len, G_KEY_FILE_NONE, gerror)) {
156 gerror.throwError("parsing ESource .ini data");
158 PlainGStrArray keys(g_key_file_get_keys(keyfile, mainSection, NULL, gerror));
160 gerror.throwError("listing keys in main section");
162 for (int i = 0; keys.at(i); i++) {
163 if (boost::starts_with(keys.at(i), "DisplayName[")) {
164 if (!g_key_file_remove_key(keyfile, mainSection, keys.at(i), gerror)) {
165 gerror.throwError("remove key");
169 g_key_file_set_string(keyfile, mainSection, "DisplayName", database.m_name.c_str());
170 g_key_file_set_boolean(keyfile, mainSection, "Enabled", true);
171 ini = g_key_file_to_data(keyfile, &len, NULL);
172 const char *configDir = g_get_user_config_dir();
174 std::string filename;
177 // Create sources dir. It might have been removed (for example, while
178 // testing) without having been recreated by evolution-source-registry.
179 std::string sourceDir = StringPrintf("%s/evolution/sources",
183 // Create unique ID if necessary.
185 uid = database.m_uri.empty() ?
188 filename = StringPrintf("%s/%s.source", sourceDir.c_str(), uid.c_str());
189 fd = ::open(filename.c_str(),
190 O_WRONLY|O_CREAT|O_EXCL,
195 if (errno == EEXIST) {
196 if (!database.m_uri.empty()) {
197 SE_THROW(StringPrintf("ESource UUID %s already in use", database.m_uri.c_str()));
199 // try again with new random UUID
202 SE_THROW(StringPrintf("creating %s failed: %s", filename.c_str(), strerror(errno)));
205 ssize_t written = write(fd, ini.get(), len);
206 int res = ::close(fd);
207 if (written != (ssize_t)len || res) {
208 SE_THROW(StringPrintf("writing to %s failed: %s", filename.c_str(), strerror(errno)));
211 // We need to wait until ESourceRegistry notices the new file.
212 SE_LOG_DEBUG(getDisplayName(), "waiting for ESourceRegistry to notice new ESource %s", uid.c_str());
213 while (!ESourceCXX(e_source_registry_ref_source(registry, uid.c_str()), TRANSFER_REF)) {
214 // This will block forever if called from the non-main-thread.
216 g_main_context_iteration(NULL, true);
218 SE_LOG_DEBUG(getDisplayName(), "ESourceRegistry has new ESource %s", uid.c_str());
220 // Try triggering that by attempting to create an ESource with the same
221 // UUID. Does not work! evolution-source-registry simply overwrites the
222 // file that we created earlier.
223 // ESourceCXX source(e_source_new_with_uid(uid.c_str(),
226 // e_source_set_display_name(source, "syncevolution-fake");
227 // e_source_set_parent(source, "local-stub");
228 // ESourceListCXX sources;
229 // sources.push_back(source.ref()); // ESourceListCXX unrefs sources it points to
230 // if (!e_source_registry_create_sources_sync(registry,
234 // gerror.throwError(StringPrintf("creating EDS database of type %s with name '%s'%s%s",
235 // sourceExtension(),
236 // database.m_name.c_str(),
237 // database.m_uri.empty() ? "" : " and URI ",
238 // database.m_uri.c_str()));
240 // SE_THROW("creating syncevolution-fake ESource succeeded although it should have failed");
243 return Database(database.m_name, uid);
246 void EvolutionSyncSource::deleteDatabase(const std::string &uri, RemoveData removeData)
248 ESourceRegistryCXX registry = EDSRegistryLoader::getESourceRegistry();
249 ESourceCXX source(e_source_registry_ref_source(registry, uri.c_str()), TRANSFER_REF);
251 throwError(StringPrintf("EDS database with URI '%s' cannot be deleted, does not exist",
255 if (!e_source_remove_sync(source, NULL, gerror)) {
256 throwError(StringPrintf("deleting EDS database with URI '%s'", uri.c_str()),
259 if (removeData == REMOVE_DATA_FORCE) {
260 // Don't wait for evolution-source-registry cache-reaper to
261 // run, instead remove files ourselves. The reaper runs only
262 // once per day and also only moves the data into a trash
263 // folder, were it would linger until finally removed after 30
266 // This is equivalent to "rm -rf $XDG_DATA_HOME/evolution/*/<uuid>".
267 std::string basedir = StringPrintf("%s/evolution", g_get_user_data_dir());
268 if (isDir(basedir)) {
269 BOOST_FOREACH (const std::string &kind, ReadDir(basedir)) {
270 std::string subdir = basedir + "/" + kind;
272 BOOST_FOREACH (const std::string &source, ReadDir(subdir)) {
273 // We assume that the UUID of the database
274 // consists only of characters which can be
275 // used in the directory name, i.e., no
276 // special encoding of the directory name.
278 rm_r(subdir + "/" + source);
279 // Keep searching, just in case, although
280 // there should only be one.
290 #endif // USE_EDS_CLIENT
292 ESource *EvolutionSyncSource::findSource(const ESourceListCXX &list, const string &id )
298 // Nothing selected specifically, use the one marked as default.
299 BOOST_FOREACH(const Database &db, getDatabases()) {
300 if (db.m_isDefault) {
307 #ifdef USE_EDS_CLIENT
308 BOOST_FOREACH (ESource *source, list) {
310 !finalID.compare(e_source_get_display_name(source)) ||
311 !finalID.compare(e_source_get_uid(source));
317 for (GSList *g = e_source_list_peek_groups (list.get()); g; g = g->next) {
318 ESourceGroup *group = E_SOURCE_GROUP (g->data);
320 for (s = e_source_group_peek_sources (group); s; s = s->next) {
321 ESource *source = E_SOURCE (s->data);
322 GStringPtr uri(e_source_get_uri(source));
323 bool found = finalID.empty() ||
324 !finalID.compare(e_source_peek_name(source)) ||
325 (uri && !finalID.compare(uri));
335 void EvolutionSyncSource::throwError(const string &action, GErrorCXX &gerror)
340 gerrorstr += gerror->message;
342 gerrorstr = ": failure";
345 throwError(action + gerrorstr);