1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: s; c-basic-offset: 4 -*- */
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
27 #include "wvlinklist.h"
28 #include "wvstringlist.h"
29 #include "wvistreamlist.h"
30 #include "wvglibstreamclone.h"
32 #include "wvlogfile.h"
34 #include "uniconfroot.h"
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>
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>
63 #include "exchangeitaccountcontrol.h"
64 #include "exchangeitaccount.h"
65 #include "exchangeitstorage.h"
66 #include "wvglibstreamclone.h"
67 #include "wvexcconn.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
)
91 CORBA_exception_init(&ev
);
93 any
._type
= TC_GNOME_Evolution_Storage_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");
113 log("%s is not a valid folder; skipping", evopath
);
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';
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
)
132 WvString
storage_path("%s" STORAGE_PATH
"%s", g_get_home_dir(), path
);
134 if ((dir
= opendir(storage_path
.cstr()))) {
136 struct dirent
*file
= readdir(dir
);
139 if (file
->d_name
[0] == '.')
141 if (file
->d_name
[1] == '\0'
142 || (file
->d_name
[1] == '.' && file
->d_name
[2] == '\0'))
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());
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
, '/')))
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
);
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);
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
);
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.");
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
,
248 "%s cannot be deleted.", path
+ 1);
249 gtk_dialog_run(GTK_DIALOG(msg
));
250 gtk_widget_destroy(msg
);
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
, '/')))
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.");
291 sync_folder(storage
, dest
, dest_path
, strrchr(dest_path
, '/') + 1);
292 sync_evo_to_fs(storage
, dest_path
);
295 evolution_storage_removed_folder(storage
, src_path
);
299 static void properties_response(GtkDialog
*dlg
, int button
, void *data
)
302 case GTK_RESPONSE_OK
:
304 case GTK_RESPONSE_CANCEL
:
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
,
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
)
365 static void owner_unset_cb (EvolutionShellComponent
*shell_component
,
368 g_timeout_add(500, quit_cb
, NULL
);
371 static void interactive_cb(EvolutionShellComponent
*shell_component
,
372 gboolean on
, gulong new_view_xid
,
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
,
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..
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
,
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
),
433 g_object_weak_ref(G_OBJECT(control
), config_destroy
, tmp_account
);
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");
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.");
487 WvString
meta("%s/.meta", pub
.cstr());
488 FILE *fp
= fopen(meta
.cstr(), "wt");
489 fprintf(fp
, "\n\npublic-folder\n");
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))
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
}
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"),
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
,
555 g_signal_connect(shell_component
, "owner_set", G_CALLBACK(owner_set_cb
),
557 g_signal_connect(shell_component
, "owner_unset", G_CALLBACK(owner_unset_cb
),
559 g_signal_connect(shell_component
, "interactive", G_CALLBACK(interactive_cb
),
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");
571 // Initialize the account handler stuff...
575 if (!setup_config(account
))
578 conn
= exchangeit_connection_init(account
, &storage
, false);
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");
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");
596 account
->save(gconf
);
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");
607 /* extern end brace */