Handle the EINTR error when doing a waitpid() in wvmagicloopback.
[wvapps.git] / evolution / exchangeitstorage.cc
blob495e8b0841eb0929330b5a170b9c053a3ba1c57f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * Authors : Patrick Patterson <ppatters@nit.ca>
4 * Peter Colijn <pcolijn@nit.ca>
5 * Scott MacLean <scott@nit.ca>
6 * William Lachance <wlach@nit.ca>
8 * Copyright 2003-2004, Net Integration Technologies, Inc.
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA
25 extern "C" {
26 #define delete _delete
27 #include <ebook/e-book.h>
28 #undef delete
31 #include "exchangeitstorage.h"
32 #include "localstorage.h"
33 #include "calendaradaptor.h"
34 #include "contactsadaptor.h"
35 #include "tasksadaptor.h"
36 #include "evoutils.h"
37 #include "eituniconfkeys.h"
39 #include "wvfile.h"
41 #include <glib.h>
42 #include <sys/stat.h>
43 #include <sys/types.h>
45 WvString get_folder_owner(WvStringParm folder_name)
47 WvStringList keysplit;
49 keysplit.split(folder_name, ".");
51 if (keysplit.count() >= 1)
52 return *(keysplit.first());
54 return WvString::null;
57 WvString reparent_folder_name(WvStringParm folder_name, WvStringParm new_owner)
59 WvString new_folder_name = new_owner;
60 WvStringList keysplit;
62 keysplit.split(folder_name, ".");
63 if (keysplit.count() < 1)
64 return WvString::null;
66 WvStringList::Iter i(keysplit);
67 i.rewind(); i.next(); // skip the old folder owner
68 while (i.next())
70 new_folder_name.append(".%s", i());
73 return new_folder_name;
76 bool is_folder_local(WvStringParm folder_name, WvStringParm storage_owner)
78 WvStringList keysplit;
80 keysplit.split(folder_name, ".");
82 if (keysplit.count() >= 1 && *(keysplit.first()) == storage_owner)
83 return true;
85 return false;
88 bool is_folder_valid(WvStringParm folder_name)
90 WvStringList keysplit;
92 keysplit.split(folder_name, ".");
93 if (keysplit.count() > 1)
94 return true;
96 return false;
99 ExchangeItStorage::ExchangeItStorage(EvolutionStorage *evo_stor,
100 WvStringParm _base_path,
101 WvStringParm _owner,
102 UniConf root) :
103 evo_storage(evo_stor),
104 base_path(_base_path),
105 log("ExchangeItStorage", WvLog::Debug1),
106 dict(100),
107 owner(_owner),
108 state(root)
112 ExchangeItStorage::~ExchangeItStorage()
114 state.commit();
117 bool ExchangeItStorage::is_ready() const
119 ExchangeItAdaptorDict::Iter i(dict);
120 for (i.rewind(); i.next(); )
122 if (!i->is_ready())
123 return false;
126 return true;
129 bool ExchangeItStorage::migrate_account(WvStringParm new_owner)
131 WvStringList folders_to_be_migrated;
132 WvStringList folders_to_be_removed;
134 log("Migrating account\n");
135 if (!is_ready())
137 log("Not ready to migrate account; not all adaptors are ready!\n");
138 return false;
141 UniConf::Iter i(state[FOLDERS_KEY]);
142 for (i.rewind(); i.next(); )
144 WvString folder = i().key().printable();
146 if((strncmp(owner, folder, owner.len()) == 0) &&
147 (folder.len() > owner.len()) &&
148 (folder[owner.len()] == '.'))
150 log("Folder %s belonged to user %s (the old user). "
151 "Keeping it as part of acct migration\n",
152 folder, owner);
153 folders_to_be_migrated.append(folder);
155 else if (((strncmp(owner, folder, owner.len()) == 0) &&
156 (folder.len() == owner.len())))
158 log("Folder %s is the root folder of user %s (the old user). "
159 "It will be destroyed automatically.\n",
160 folder, owner);
162 else
164 log("Folder %s didn't belong to user %s (%s %s %s). "
165 "Deleting it as part of acct migration.\n",
166 folder, owner,
167 (strncmp(owner, folder, owner.len()) == 0),
168 (folder.len() > owner.len()),
169 (folder[owner.len()] == '.'));
171 folders_to_be_removed.append(folder);
176 // all the folders which belonged to the old owner should
177 // be renamed and reallocated to the new owner
178 WvStringList::Iter j(folders_to_be_migrated);
179 for (j.rewind(); j.next();)
181 WvStringParm old_owner = get_folder_owner(j());
182 WvString new_folder_name = j();
183 if (old_owner != new_owner)
185 log("Folder %s belonged to user %s (the old user). "
186 "Renaming it to belong to the new owner.\n",
187 j(), owner);
188 new_folder_name = reparent_folder_name(j(), new_owner);
189 log("rename_local_folder(%s, %s) (new owner: %s)\n",
190 j(), new_folder_name,
191 new_owner);
192 if (!!new_folder_name)
193 rename_local_folder(j(), new_folder_name); // should never fail
194 else
195 log("Not renaming folder (%s): it has no name!\n",
196 j(), WvLog::Warning);
200 // Remove public folders that belong to the "old"
201 // so we won't have any weird "public" folders laying about
202 log("Purging any public folders belonging to the old user\n");
203 WvStringList::Iter k(folders_to_be_removed);
204 for (k.rewind(); k.next();)
206 if (!remove_folder(k()))
207 log("Couldn't remove folder?\n");
210 ExchangeItAdaptorDict::Iter l(dict);
211 for (l.rewind(); l.next();)
213 WvStringList *uids = l->get_item_uids("all");
214 WvStringList::Iter m(*uids);
215 for (m.rewind(); m.next();)
217 log("Queing item %s in folder %s to be resent to the server\n",
218 m(), l->get_key());
219 state[l->get_key()][TOSEND_KEY][m()].setmeint(1);
221 delete uids;
224 owner = new_owner;
225 return true;
228 bool ExchangeItStorage::rename_local_folder(WvStringParm old_key,
229 WvStringParm new_key)
231 ExchangeItAdaptor *adaptor = dict[old_key];
232 if (!adaptor)
233 return false;
235 dict.set_autofree(adaptor, false);
236 dict.remove(adaptor);
237 adaptor->set_key(new_key);
238 dict.add(adaptor, true);
240 WvStringParm type = state[FOLDERS_KEY][old_key].getme();
241 state[FOLDERS_KEY][old_key].remove();
242 state[FOLDERS_KEY][new_key].setme(type);
244 state[new_key][VERSION_KEY].setmeint(0);
245 UniConf::Iter j(state[old_key][CL2SRV_KEY]);
246 for (j.rewind(); j.next(); )
248 state[new_key][CL2SRV_KEY][j().key()].setme(j().getme());
251 log("rename_local_folder: version of old key is now: %s\n",
252 state[old_key][VERSION_KEY].getme());
253 state[old_key][VERSION_KEY].remove();
254 state[old_key].remove();
255 log("rename_local_folder: version of old key is now: %s\n",
256 state[old_key][VERSION_KEY].getme());
258 state[FOLDERS_KEY][TOADD_KEY][new_key].setme(dict[new_key]->get_type());
260 return true;
263 void ExchangeItStorage::clean_folder_hierarchy_upwards(WvStringParm path)
265 UniConf folder_info = state[EVO_FOLDERS_KEY][path];
267 unsigned int num_keys = 0;
268 UniConf::Iter c(folder_info);
269 for (c.rewind(); c.next(); )
270 ++num_keys;
272 WvString type = folder_info[TYPE_KEY].getme();
274 if ((type == WvString::null && num_keys == 0)
275 || (type == "folder" && num_keys <= 3))
277 evolution_storage_removed_folder(evo_storage, path);
278 folder_info.setme(WvString::null);
280 WvString parent;
281 WvStringList folders;
282 folders.split(path, "/");
283 folders.popstr(); // stupid leading blank (from the leading slash)
284 while (folders.count() > 1)
285 parent.append("/%s", folders.popstr());
287 if (!!parent)
288 clean_folder_hierarchy_upwards(parent);
292 void ExchangeItStorage::sync_folder_hierarchy(WvStringParm path)
294 if (evolution_storage_folder_exists(evo_storage, path))
295 evolution_storage_removed_folder(evo_storage, path);
297 UniConf folder_info = state[EVO_FOLDERS_KEY][path];
299 WvString type = folder_info[TYPE_KEY].getme();
300 WvString desc = folder_info[DESC_KEY].getme();
301 WvString icon = folder_info[ICON_KEY].getme();
303 if (type == WvString::null)
305 if (path == "/Public Folders")
307 type = "public-folder";
308 desc = "public-folder";
309 icon = "public-folder";
311 else
313 type = "folder";
314 desc = "folder";
315 icon = "folder";
319 WvString phys_uri("file://%s%s", base_path, path);
320 mkdir(phys_uri.cstr() + 7, 0770);
322 if (type == "mail")
324 WvString mbox("%s/mbox", phys_uri.cstr() + 7);
325 FILE *fp = fopen(mbox.cstr(), "at");
326 if (!fp)
327 log("Cannot create dummy mailbox (uri: %s)\n", mbox, WvLog::Error);
328 else
329 fclose(fp);
332 const char *path_top = strrchr(path, '/');
333 assert(path_top && *(path_top + 1));
334 evolution_storage_new_folder(evo_storage, path, path_top + 1,
335 type, phys_uri, desc, icon, 0, FALSE, 0);
337 UniConf::Iter i(folder_info);
338 for (i.rewind(); i.next(); )
340 WvString key = i().key().printable();
342 if (key == "desc" || key == "type" || key == "icon")
343 continue;
345 WvString build_path("%s/%s", path, key);
346 sync_folder_hierarchy(build_path);
350 bool ExchangeItStorage::load_folders()
352 UniConf::Iter i(state[FOLDERS_KEY]);
353 for (i.rewind(); i.next(); )
355 log("load_local_folders(): adding folder %s (type: %s)\n",
356 i().key().printable(), i().getme());
358 //if (is_folder_local(i().key().printable(), owner))
359 add_folder(i().key().printable(), i().getme());
362 return true;
365 bool ExchangeItStorage::add_folder(WvStringParm key, WvStringParm type,
366 const bool by_client_request)
368 if (!is_folder_valid(key))
369 return false;
371 if (dict[key]) // already loaded, do nothing
372 return true;
374 WvString path = convert_eitpath_to_evopath(key, owner);
375 WvString rebuild_path = path;
376 const bool local = is_folder_local(key, owner);
378 if (!local)
380 WvStringList folders;
381 folders.split(path, "/");
382 WvStringList::Iter i(folders);
383 rebuild_path = "";
384 for (i.rewind(); i.next(); )
386 if (i() != "")
388 rebuild_path.append("/%s", i());
389 if (!state[EVO_FOLDERS_KEY][rebuild_path].exists())
390 break;
395 WvString evo_type;
396 WvString desc;
397 if (type == CALENDAR_TYPE)
399 evo_type = "calendar";
400 desc = "Calendar Items";
402 else if (type == CONTACTS_TYPE)
404 evo_type = "contacts";
405 desc = "Contacts";
407 else if (type == TASKS_TYPE)
409 evo_type = "tasks";
410 desc = "Tasks";
412 else
414 evo_type = "mail";
415 desc = "Mail";
418 state[FOLDERS_KEY][key].setme(type);
419 if (by_client_request)
420 state[FOLDERS_KEY][TOADD_KEY][key].setme(type);
422 state[EVO_FOLDERS_KEY][path][TYPE_KEY].setme(evo_type);
423 state[EVO_FOLDERS_KEY][path][DESC_KEY].setme(desc);
424 state[EVO_FOLDERS_KEY][path][ICON_KEY].setme("");
426 sync_folder_hierarchy(rebuild_path);
428 WvString phys_path("%s%s", base_path, path);
430 ExchangeItAdaptor *adaptor = NULL;
431 if (type == CALENDAR_TYPE)
433 adaptor = new CalendarAdaptor(key, phys_path, state);
434 if (key == WvString("%s.Calendar", owner))
436 if(!state[CALENDAR_LOCAL_IMPORTED_KEY].getmeint())
438 log("migrating calendars !!\n");
439 import_local_calendar(static_cast<CalendarAdaptor *>(adaptor),
440 WvString("file://%s/evolution/local/Calendar/calendar.ics",
441 WvString(g_get_home_dir())));
446 else if (type == CONTACTS_TYPE)
448 adaptor = new ContactsAdaptor(key, phys_path, state);
449 if (key == WvString("%s.Contacts", owner))
451 if(!state[CONTACTS_LOCAL_IMPORTED_KEY].getmeint())
453 log("migrating contacts !!\n");
455 Here, we hardcode the local contact DB path name
456 Don't worry, this is how it is done in
457 evolution/addressbook/backend/ebook/load-gnomecard-addressbook.c
458 -- hub
460 import_local_contacts(static_cast<ContactsAdaptor *>(adaptor),
461 WvString("file://%s/evolution/local/Contacts/addressbook.db",
462 WvString(g_get_home_dir())));
466 else if (type == TASKS_TYPE)
468 adaptor = new TasksAdaptor(key, phys_path, state);
469 if (key == WvString("%s.Tasks", owner))
471 if(!state[TASKS_LOCAL_IMPORTED_KEY].getmeint())
473 log("migrating tasks !!\n");
474 import_local_calendar(static_cast<CalendarAdaptor *>(adaptor),
475 WvString("file://%s/evolution/local/Tasks/tasks.ics",
476 WvString(g_get_home_dir())));
481 else
482 adaptor = new NullAdaptor(key, phys_path, state);
484 if (adaptor)
485 dict.add(adaptor, true);
487 return true;
490 bool ExchangeItStorage::remove_folder(WvStringParm key,
491 const bool by_client_request)
493 log("removing folder %s (by client request: %s)\n",
494 key, by_client_request);
495 bool retval = true;
497 ExchangeItAdaptor *adaptor = dict[key];
498 if (adaptor)
499 dict.remove(adaptor);
501 WvString path;
502 WvString phys_path;
503 path = convert_eitpath_to_evopath(key, owner);
504 if (!path)
506 // a path with a single token: exchangeit will send us these
507 // suckers, but we shouldn't actually be doing anything with
508 // them. its existence is a non-critical error
509 log("Tried to remove a folder with one (or less) token. "
510 "Shouldn't happen. eitpath: %s owner: %s\n",
511 key, owner, WvLog::Warning);
512 return false;
514 state[FOLDERS_KEY][key].setme(WvString::null);
515 state[key].setme(WvString::null);
517 phys_path = WvString("%s%s", base_path, path);
519 // if this was done by client request (we assume that any other client
520 // was sane enough to do this itself) purge any folders which lie below
521 // this one in the hierarchy (if it turns out we do not have sufficient
522 // permission to do so, we can always recreate them later)
523 if (by_client_request)
525 UniConf::Iter i(state[FOLDERS_KEY]);
526 for (i.rewind(); i.next(); )
528 WvStringParm key2 = i().key().printable();
530 // WLACH: this should be safe
531 if (key2.len() > key.len() &&
532 !strncmp(key.cstr(), key2.cstr(), key.len()),
533 key2.cstr()[key.len()] == '.')
535 if (!remove_folder(key2, false))
536 retval = false;
538 state[FOLDERS_KEY][TODELETE_KEY][key2].setmeint(1);
543 state[EVO_FOLDERS_KEY][path].setme(WvString::null);
544 if (by_client_request)
545 state[FOLDERS_KEY][TODELETE_KEY][key].setmeint(1);
546 log("remove_folder: removing storage folder: %s\n", path);
547 evolution_storage_removed_folder(evo_storage, path);
548 log("remove_folder: removing phys path: %s\n", phys_path);
549 destroy_dir(phys_path);
551 clean_folder_hierarchy_upwards(path);
553 return retval;
556 bool ExchangeItStorage::folder_exists(WvStringParm key)
558 ExchangeItAdaptor *adaptor = dict[key];
559 if (adaptor)
560 return true;
562 return false;
566 /** open callback for the CalClient to migrate data from
568 static void import_opened_cb (CalClient *local_client, CalClientOpenStatus status, gpointer data)
570 CalendarAdaptor *adaptor = static_cast<CalendarAdaptor *>(data);
571 if (status != CAL_CLIENT_OPEN_SUCCESS)
573 g_object_unref(local_client);
574 return;
576 adaptor->merge_into_calendar(local_client);
581 /** Copy over local calendar data to ExchangeIt.
582 * @param local_cal the uri of the .ics file to be merged
583 * with ExchangeIt data.
585 void ExchangeItStorage::import_local_calendar(CalendarAdaptor *adaptor,
586 WvStringParm local_cal)
588 CalClient *local_client = cal_client_new();
590 g_signal_connect (local_client, "cal_opened",
591 G_CALLBACK(import_opened_cb),
592 static_cast<void*>(adaptor));
594 if (!cal_client_open_calendar(local_client, local_cal, true))
596 log("%s do not exist. No migration needed.\n");
601 static void import_ebook_opened_cb(EBook *book, EBookStatus status,
602 gpointer data)
604 ContactsAdaptor *adaptor = static_cast<ContactsAdaptor *>(data);
605 if (status != E_BOOK_STATUS_SUCCESS)
607 g_object_unref(book);
608 return;
610 adaptor->merge_into_contacts(book);
613 void ExchangeItStorage::import_local_contacts(ContactsAdaptor *adaptor,
614 WvStringParm local_ebook)
616 EBook *book = e_book_new();
618 e_book_load_uri(book, local_ebook, import_ebook_opened_cb, adaptor);