schedulator: only count bugs that you were the *last* one to resolve, but
[wvapps.git] / evolution / main.cc
blob19e8e07992455da60f3d3926ed90cc7785963f06
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: s; c-basic-offset: 4 -*- */
2 /*
3 * Authors : William Lachance <wlach@net-itech.com>
4 * Peter Colijn <pcolijn@net-itech.com>
6 * Based on code written by JP Rosevear <jpr@ximian.com>
8 * Copyright 2003-2004, Net Integration Technologies, Inc.
9 * Copyright 2003, Novell, Inc.
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of version 2 of the GNU General Public
13 * License as published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
26 #include "wvstring.h"
27 #include "wvlinklist.h"
28 #include "wvstringlist.h"
29 #include "wvistreamlist.h"
30 #include "wvglibstreamclone.h"
31 #include "wvcrash.h"
32 #include "wvlogfile.h"
34 #include "uniconfroot.h"
36 #include <string.h>
38 extern "C"
40 #include <bonobo-activation/bonobo-activation.h>
41 #include <bonobo/bonobo-main.h>
42 #include <bonobo/bonobo-generic-factory.h>
43 #include <bonobo/bonobo-moniker-util.h>
44 #include <bonobo/bonobo-exception.h>
45 #include <libgnomeui/gnome-ui-init.h>
46 #include <libgnomevfs/gnome-vfs-init.h>
47 #include <gconf/gconf-client.h>
48 #include <gtk/gtkmessagedialog.h>
49 #include <gtk/gtk.h>
50 #include <glade/glade.h>
52 #include <e-util/e-passwords.h>
53 #include <pcs/cal-factory.h>
54 #include <shell/evolution-shell-component.h>
55 #include <shell/evolution-shell-component-client.h>
56 #include <shell/evolution-storage.h>
57 #include <shell/evolution-storage-listener.h>
59 #include <dirent.h>
60 #include <unistd.h>
63 #include "exchangeitaccountcontrol.h"
64 #include "exchangeitaccount.h"
65 #include "exchangeitstorage.h"
66 #include "wvglibstreamclone.h"
67 #include "wvexcconn.h"
68 #include "evoutils.h"
70 #define EXCHANGEIT_COMPONENT_ID "OAFIID:GNOME_Evolution_ExchangeIT_ShellComponent"
71 #define EXCHANGEIT_ACCOUNT_CONTROL_ID "OAFIID:GNOME_Evolution_ExchangeIT_AccountConfigControl"
72 #define EXCHANGEIT_CONFIG_FACTORY_ID "OAFIID:GNOME_Evolution_ExchangeIT_ConfigFactory"
74 #define STORAGE_PATH "/evolution/exchange-it"
76 const char * EXCHANGEIT_CALENDAR_TYPE = "IPF.Appointment";
77 const char * EXCHANGEIT_NULL_TYPE = "*";
78 const char * EXCHANGEIT_CONTACTS_TYPE = "IPF.Contact";
79 const char * EXCHANGEIT_TASKS_TYPE = "IPF.Task";
81 static BonoboGenericFactory *config_factory = NULL;
82 const char * public_folders_path = "/Public Folders/";
84 /* this function lifted from evolution code: mail/component-factory.h */
85 static void notify_listener(const Bonobo_Listener listener,
86 GNOME_Evolution_Storage_Result result)
88 CORBA_any any;
89 CORBA_Environment ev;
91 CORBA_exception_init(&ev);
93 any._type = TC_GNOME_Evolution_Storage_Result;
94 any._value = &result;
96 Bonobo_Listener_event(listener, "result", &any, &ev);
98 CORBA_exception_free(&ev);
101 static void sync_folder(EvolutionStorage *storage, WvString &fspath,
102 const char *evopath, const char *name)
104 WvLog log("sync_folder");
106 WvString uri("file://%s", fspath.cstr());
107 WvString meta_file("%s/.meta", fspath.cstr());
108 char desc[256] = {'\0'}, type[256] = {'\0'}, icon[256] = {'\0'};
110 FILE *fp = fopen(meta_file.cstr(), "rt");
111 if (!fp)
113 log("%s is not a valid folder; skipping", evopath);
114 return;
117 fgets(type, 255, fp);
118 type[strlen(type) - 1] = '\0';
119 fgets(desc, 255, fp);
120 desc[strlen(desc) - 1] = '\0';
121 fgets(icon, 255, fp);
122 icon[strlen(icon) - 1] = '\0';
123 fclose(fp);
125 evolution_storage_new_folder(storage, evopath, name,
126 type, uri.cstr(), desc, icon, 0, FALSE, 0);
129 static void sync_evo_to_fs(EvolutionStorage *storage, const char *path)
131 DIR *dir;
132 WvString storage_path("%s" STORAGE_PATH "%s", g_get_home_dir(), path);
134 if ((dir = opendir(storage_path.cstr()))) {
135 struct stat st;
136 struct dirent *file = readdir(dir);
137 while (file)
139 if (file->d_name[0] == '.')
141 if (file->d_name[1] == '\0'
142 || (file->d_name[1] == '.' && file->d_name[2] == '\0'))
144 file = readdir(dir);
145 continue;
149 WvString file_path("%s/%s", storage_path, file->d_name);
150 stat(file_path.cstr(), &st);
151 if (S_ISDIR(st.st_mode))
153 WvString evo_path("%s/%s", path, file->d_name);
154 if (!evolution_storage_folder_exists(storage, evo_path.cstr()))
156 sync_folder(storage, file_path, evo_path, file->d_name);
158 sync_evo_to_fs(storage, evo_path.cstr());
161 file = readdir(dir);
166 static void create_folder_cb(EvolutionStorage *storage, Bonobo_Listener listener,
167 const char *path, const char *type, const char *desc,
168 const char *uri, void *data)
170 WvLog log("create_folder_cb");
171 ExchangeItStorage *eit_storage = static_cast<ExchangeItStorage *>(data);
172 GNOME_Evolution_Storage_Result result = GNOME_Evolution_Storage_PERMISSION_DENIED;
174 log("Creating folder %s, of type%s\n", path, type);
176 if (eit_storage->get_owner())
178 // FIXME: What to do with folder names with '.' in them?
179 WvString eit_path(path);
180 char *p = eit_path.edit();
181 while ((p = strchr(p, '/')))
183 *p = '.';
186 WvString eit_key("%s.%s", eit_storage->get_owner(), eit_path.cstr() + 1);
187 if (!eit_storage->get_dict()[eit_key])
189 UniConf toadd_key = eit_storage->get_state()["folders"]["toadd"][eit_key];
190 if (!strcmp(type, "calendar"))
191 toadd_key.set(EXCHANGEIT_CALENDAR_TYPE);
192 else if (!strcmp(type, "mail") || !strcmp(type, "folder"))
193 toadd_key.set(EXCHANGEIT_NULL_TYPE);
194 else if (!strcmp(type, "contacts"))
195 toadd_key.set(EXCHANGEIT_CONTACTS_TYPE);
196 else if (!strcmp(type, "tasks"))
197 toadd_key.set(EXCHANGEIT_TASKS_TYPE);
199 FILE *fp;
200 WvString real_uri("file://%s" STORAGE_PATH "%s", g_get_home_dir(), path);
201 WvString meta_file("%s" STORAGE_PATH "/.meta", g_get_home_dir());
203 if (mkdir(real_uri.cstr() + 7, 0770) != 0)
205 log("Cannot create folder %s on filesystem.", path + 1);
206 return;
209 if (!(fp = fopen(meta_file.cstr(), "wt")))
211 g_warning("Unable to write folder information.");
214 fprintf(fp, "%s\n%s\n\n", type, desc);
215 fclose(fp);
217 if (!strcmp(type, "mail"))
219 WvString mbox("%s" STORAGE_PATH "%s/mbox", g_get_home_dir(), path);
220 if (!(fp = fopen(mbox.cstr(), "at")))
222 log("Cannot create dummy mailbox.");
224 fclose(fp);
227 evolution_storage_new_folder(storage, path, strrchr(path, '/') + 1,
228 type, real_uri.cstr(), desc, NULL, 0, FALSE, 0);
229 result = GNOME_Evolution_Storage_OK;
233 notify_listener(listener, result);
236 static void remove_folder_cb(EvolutionStorage *storage, Bonobo_Listener listener,
237 const char *path, const char *uri, void *data)
239 WvLog log("remove_folder_cb");
240 ExchangeItStorage *eit_storage = static_cast<ExchangeItStorage *>(data);
242 if (!strcmp(path, "/Public Folders"))
244 GtkWidget *msg = gtk_message_dialog_new(NULL,
245 GTK_DIALOG_DESTROY_WITH_PARENT,
246 GTK_MESSAGE_ERROR,
247 GTK_BUTTONS_CLOSE,
248 "%s cannot be deleted.", path + 1);
249 gtk_dialog_run(GTK_DIALOG(msg));
250 gtk_widget_destroy(msg);
251 return;
254 log("Removing folder %s\n", path);
256 if (eit_storage->get_owner())
258 // FIXME: What to do with folder names with '.' in them?
259 WvString eit_path(path);
260 char *p = eit_path.edit();
261 while ((p = strchr(p, '/')))
263 *p = '.';
266 WvString eit_key("%s.%s", eit_storage->get_owner(), eit_path.cstr() + 1);
267 eit_storage->get_state()["folders"]["todelete"][eit_key].setint(1);
269 WvString real_path("%s" STORAGE_PATH "%s", g_get_home_dir(), path);
270 destroy_dir(real_path);
271 evolution_storage_removed_folder(storage, path);
275 static void xfer_folder_cb(EvolutionStorage *storage, Bonobo_Listener listener,
276 const char *src_path, const char *dest_path,
277 gboolean remove_src, void *data)
279 WvLog log("xfer_folder_cb");
280 const char *cmd = remove_src ? "mv " : "cp -a ";
281 WvString dest("%s" STORAGE_PATH "%s", g_get_home_dir(), dest_path);
282 WvString cmd_line("%s%s" STORAGE_PATH "%s %s",
283 cmd, g_get_home_dir(), src_path, dest.cstr());
285 if (system(cmd_line) != 0)
287 log("An error occured while moving or copying folders.");
288 return;
291 sync_folder(storage, dest, dest_path, strrchr(dest_path, '/') + 1);
292 sync_evo_to_fs(storage, dest_path);
293 if (remove_src)
295 evolution_storage_removed_folder(storage, src_path);
299 static void properties_response(GtkDialog *dlg, int button, void *data)
301 switch (button) {
302 case GTK_RESPONSE_OK:
303 break;
304 case GTK_RESPONSE_CANCEL:
305 break;
308 gtk_widget_destroy(GTK_WIDGET(dlg));
311 static void show_properties_cb(EvolutionStorage *storage, const char *path,
312 unsigned int item, unsigned long parent)
314 GtkWidget *dlg = gtk_dialog_new();
315 GtkWidget *lbl = gtk_label_new("Things will go here to do stuff.");
316 WvString title("Permissions for %s", strrchr(path, '/') + 1);
317 gtk_dialog_add_buttons(GTK_DIALOG(dlg),
318 GTK_STOCK_OK, GTK_RESPONSE_OK,
319 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
320 NULL);
321 gtk_window_set_title(GTK_WINDOW(dlg), title.cstr());
322 g_signal_connect(dlg, "response", G_CALLBACK(properties_response), NULL);
323 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dlg)->vbox), lbl);
324 gtk_widget_show_all(dlg);
327 static void owner_set_cb(EvolutionShellComponent *shell_component,
328 EvolutionShellClient *shell_client,
329 const char *evo_dir, gpointer user_data)
331 WvLog log("owner_set_cb");
333 EvolutionStorage *storage = evolution_storage_new("ExchangeIT", TRUE);
335 evolution_storage_add_property_item(storage, "Permissions...",
336 "Change permissions for this folder", NULL);
338 g_signal_connect(storage, "create_folder",
339 G_CALLBACK(create_folder_cb), user_data);
340 g_signal_connect(storage, "remove_folder",
341 G_CALLBACK(remove_folder_cb), user_data);
342 g_signal_connect(storage, "xfer_folder",
343 G_CALLBACK(xfer_folder_cb), user_data);
344 g_signal_connect(storage, "show_folder_properties",
345 G_CALLBACK(show_properties_cb), user_data);
347 if (evolution_storage_register_on_shell(storage,
348 evolution_shell_client_corba_objref(shell_client)) != EVOLUTION_STORAGE_OK)
350 log("Error registering custom storage on shell.");
351 bonobo_object_unref(BONOBO_OBJECT(storage));
354 sync_evo_to_fs(storage, "");
357 /* This returns %TRUE all the time, so if set as an idle callback it
358 effectively causes each and every nested glib mainloop to be quit. */
359 static gboolean quit_cb(gpointer closure)
361 bonobo_main_quit();
362 return TRUE;
365 static void owner_unset_cb (EvolutionShellComponent *shell_component,
366 gpointer user_data)
368 g_timeout_add(500, quit_cb, NULL);
371 static void interactive_cb(EvolutionShellComponent *shell_component,
372 gboolean on, gulong new_view_xid,
373 gpointer user_data)
378 static void last_calendar_gone_cb(CalFactory *factory, gpointer data)
380 /* FIXME: what to do? */
383 static void control_apply_cb(EvolutionConfigControl *config_control,
384 gpointer data)
386 EItAccountControl *eicc = EXCHANGEIT_ACCOUNT_CONTROL(config_control);
387 ExchangeItAccount *tmp_account = (ExchangeItAccount *)g_object_get_data(G_OBJECT (eicc), "tmp_account");
389 // copy the old settings into the new settings
390 ExchangeItAccount *account = static_cast<ExchangeItAccount *>(data);
391 account->set_server(tmp_account->get_server());
392 account->set_user(tmp_account->get_user());
393 account->set_password(tmp_account->get_password());
395 // FIXME: should we save the account information immediately? probably..
396 g_message("AccountInfo (%s, %s, %s)\n", account->get_server().cstr(),
397 account->get_user().cstr(),
398 account->get_password().cstr());
400 // We need to regenerate the folder list after the info is changed, I guess..
401 GtkWidget *dlg;
402 dlg = gtk_message_dialog_new (NULL, (GtkDialogFlags) (/*GTK_DIALOG_DESTROY_WITH_PARENT |*/ GTK_DIALOG_MODAL),
403 GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK,
404 "You must restart evolution in order for changes to take affect");
406 gtk_dialog_run (GTK_DIALOG (dlg));
407 gtk_widget_destroy (dlg);
410 static void config_destroy(gpointer data, GObject *where_object_was)
412 ExchangeItAccount *tmp_account = static_cast<ExchangeItAccount *>(data);
413 delete (tmp_account);
416 static BonoboObject *config_factory_func(BonoboGenericFactory *factory,
417 const char *component_id,
418 gpointer data)
420 g_message ("%s : %s", EXCHANGEIT_ACCOUNT_CONTROL_ID, component_id);
421 ExchangeItAccount *account = (ExchangeItAccount *)data;
422 ExchangeItAccount *tmp_account = new ExchangeItAccount(*account);
424 if (!strcmp(component_id, EXCHANGEIT_ACCOUNT_CONTROL_ID))
426 BonoboObject *control;
428 control = exchangeit_account_control_new(tmp_account);
430 g_object_set_data(G_OBJECT(control), "tmp_account", tmp_account);
431 g_signal_connect(control, "apply", G_CALLBACK(control_apply_cb),
432 account);
433 g_object_weak_ref(G_OBJECT(control), config_destroy, tmp_account);
435 return control;
438 return NULL;
441 static bool setup_config(ExchangeItAccount *account)
443 config_factory = bonobo_generic_factory_new(EXCHANGEIT_CONFIG_FACTORY_ID,
444 config_factory_func, account);
446 return (config_factory != CORBA_OBJECT_NIL);
449 // setup_paths: attempts to determine if the base path (~/evolution/local) exists
450 // if it does not exist, this function will attempt to create it
451 // returns true on success, false on failure
452 static bool check_paths()
454 WvString base_path("%s/evolution", g_get_home_dir());
455 WvString local_path("%s" STORAGE_PATH, g_get_home_dir());
456 WvLog log("setup_paths", WvLog::Debug1);
458 struct stat stat_buf;
460 if (stat(base_path.cstr(), &stat_buf) != 0)
462 log("Base path not found");
463 return false;
464 if (mkdir(base_path.cstr(), 0770) != 0)
466 g_message("ERROR: Couldn't create base path");
470 if (stat(local_path.cstr(), &stat_buf) != 0)
472 log("Local path not found");
473 if (mkdir(local_path.cstr(), 0770) != 0)
475 log("ERROR: Couldn't create local path");
479 WvString pub("%s" STORAGE_PATH "/Public Folders", g_get_home_dir());
481 if (stat(pub.cstr(), &stat_buf) != 0 || !S_ISDIR(stat_buf.st_mode)) {
482 if (mkdir(pub.cstr(), 0770) != 0) {
483 log("ERROR: Couldn't create Public Folders.");
484 return false;
486 else {
487 WvString meta("%s/.meta", pub.cstr());
488 FILE *fp = fopen(meta.cstr(), "wt");
489 fprintf(fp, "\n\npublic-folder\n");
490 fclose(fp);
494 return true;
497 static gboolean do_sync(gpointer data)
499 WvStream *conn = static_cast<WvStream *>(data);
500 WvLog log("do_sync", WvLog::Debug1);
502 log("Trying to sync\n");
504 if (conn && conn->select(0, true, true))
505 conn->callback();
507 log("Done sync\n");
509 return TRUE;
512 extern "C"
515 main (int argc, char **argv)
517 WvString storage_path("%s" STORAGE_PATH, g_get_home_dir());
518 ExchangeItAccount *account = NULL;
519 GConfClient *gconf = NULL;
520 EvolutionShellComponent *shell_component = NULL;
521 EvolutionShellComponentFolderType types[] = {
522 { "exchangeit-server", "none", "exchangeit-server", "ExchangeIT server", FALSE, NULL, NULL },
523 { NULL, NULL, NULL, NULL, FALSE, NULL, NULL }
525 UniConfRoot root;
527 wvcrash_setup(argv[0]);
529 // un-ifdef for file-logging nirvana
530 WvString evo_eit_log("%s/eveit", g_get_home_dir());
531 WvLogFile filelogger(evo_eit_log, WvLog::Debug5);
533 WvExcConn *conn = NULL;
534 //WvIStreamList *clone_list = new WvIStreamList;
535 //WvGlibStreamClone glibstream(NULL, clone_list);
536 gnome_program_init(PACKAGE, VERSION, LIBGNOMEUI_MODULE, argc, argv,
537 GNOME_PROGRAM_STANDARD_PROPERTIES,
538 GNOME_PARAM_HUMAN_READABLE_NAME,
539 _("Net-Itech Connector for ExchangeIT"),
540 NULL);
541 gnome_vfs_init();
542 glade_init();
543 gconf = gconf_client_get_default();
545 gconf_client_add_dir(gconf, EXCHANGEIT_GCONF_DIR,
546 GCONF_CLIENT_PRELOAD_RECURSIVE, NULL);
548 account = new ExchangeItAccount(gconf);
549 ExchangeItStorage storage(storage_path, account->get_user(), root);
551 shell_component = evolution_shell_component_new(types, NULL, NULL, NULL,
552 NULL, NULL, NULL, NULL,
553 NULL, NULL, NULL);
555 g_signal_connect(shell_component, "owner_set", G_CALLBACK(owner_set_cb),
556 &storage);
557 g_signal_connect(shell_component, "owner_unset", G_CALLBACK(owner_unset_cb),
558 NULL);
559 g_signal_connect(shell_component, "interactive", G_CALLBACK(interactive_cb),
560 NULL);
562 /* register factories */
563 int ret = bonobo_activation_active_server_register(EXCHANGEIT_COMPONENT_ID,
564 bonobo_object_corba_objref(BONOBO_OBJECT(shell_component)));
565 if (ret != Bonobo_ACTIVATION_REG_SUCCESS)
567 g_message("Unable to activate Bonobo component");
568 //goto failed;
571 // Initialize the account handler stuff...
572 if (!check_paths())
573 goto failed;
575 if (!setup_config(account))
576 goto failed;
578 conn = exchangeit_connection_init(account, &storage, false);
580 if (conn)
582 // FIXME: Is there a better way of doing this?
583 g_timeout_add(10000, do_sync, conn);
584 g_message("ExchangeIT connector up and running");
587 else
589 // FIXME: should actually pop up a dialog or something
590 g_message("Failed to create connection to ExchangeIt server\n");
591 g_message("This can often be the result of misconfigured account information.\n");
594 bonobo_main();
596 account->save(gconf);
598 return 0;
600 failed:
601 g_warning("\nCould not register Evolution ExchangeIT backend services.\n"
602 "This probably means another copy of evolution-exchangeit-storage\n"
603 "is already running.\n");
605 return 1;
607 /* extern end brace */