1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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
26 #define delete _delete
27 #include <ebook/e-book.h>
31 #include "exchangeitstorage.h"
32 #include "localstorage.h"
33 #include "calendaradaptor.h"
34 #include "contactsadaptor.h"
35 #include "tasksadaptor.h"
37 #include "eituniconfkeys.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
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
)
88 bool is_folder_valid(WvStringParm folder_name
)
90 WvStringList keysplit
;
92 keysplit
.split(folder_name
, ".");
93 if (keysplit
.count() > 1)
99 ExchangeItStorage::ExchangeItStorage(EvolutionStorage
*evo_stor
,
100 WvStringParm _base_path
,
103 evo_storage(evo_stor
),
104 base_path(_base_path
),
105 log("ExchangeItStorage", WvLog::Debug1
),
112 ExchangeItStorage::~ExchangeItStorage()
117 bool ExchangeItStorage::is_ready() const
119 ExchangeItAdaptorDict::Iter
i(dict
);
120 for (i
.rewind(); i
.next(); )
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");
137 log("Not ready to migrate account; not all adaptors are ready!\n");
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",
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",
164 log("Folder %s didn't belong to user %s (%s %s %s). "
165 "Deleting it as part of acct migration.\n",
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",
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
,
192 if (!!new_folder_name
)
193 rename_local_folder(j(), new_folder_name
); // should never fail
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",
219 state
[l
->get_key()][TOSEND_KEY
][m()].setmeint(1);
228 bool ExchangeItStorage::rename_local_folder(WvStringParm old_key
,
229 WvStringParm new_key
)
231 ExchangeItAdaptor
*adaptor
= dict
[old_key
];
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());
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(); )
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
);
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());
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";
319 WvString
phys_uri("file://%s%s", base_path
, path
);
320 mkdir(phys_uri
.cstr() + 7, 0770);
324 WvString
mbox("%s/mbox", phys_uri
.cstr() + 7);
325 FILE *fp
= fopen(mbox
.cstr(), "at");
327 log("Cannot create dummy mailbox (uri: %s)\n", mbox
, WvLog::Error
);
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")
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());
365 bool ExchangeItStorage::add_folder(WvStringParm key
, WvStringParm type
,
366 const bool by_client_request
)
368 if (!is_folder_valid(key
))
371 if (dict
[key
]) // already loaded, do nothing
374 WvString path
= convert_eitpath_to_evopath(key
, owner
);
375 WvString rebuild_path
= path
;
376 const bool local
= is_folder_local(key
, owner
);
380 WvStringList folders
;
381 folders
.split(path
, "/");
382 WvStringList::Iter
i(folders
);
384 for (i
.rewind(); i
.next(); )
388 rebuild_path
.append("/%s", i());
389 if (!state
[EVO_FOLDERS_KEY
][rebuild_path
].exists())
397 if (type
== CALENDAR_TYPE
)
399 evo_type
= "calendar";
400 desc
= "Calendar Items";
402 else if (type
== CONTACTS_TYPE
)
404 evo_type
= "contacts";
407 else if (type
== TASKS_TYPE
)
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
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())));
482 adaptor
= new NullAdaptor(key
, phys_path
, state
);
485 dict
.add(adaptor
, 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
);
497 ExchangeItAdaptor
*adaptor
= dict
[key
];
499 dict
.remove(adaptor
);
503 path
= convert_eitpath_to_evopath(key
, owner
);
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
);
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))
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
);
556 bool ExchangeItStorage::folder_exists(WvStringParm key
)
558 ExchangeItAdaptor
*adaptor
= dict
[key
];
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
);
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
,
604 ContactsAdaptor
*adaptor
= static_cast<ContactsAdaptor
*>(data
);
605 if (status
!= E_BOOK_STATUS_SUCCESS
)
607 g_object_unref(book
);
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
);