1 | /* |
---|
2 | Copyright 2000, 2001 Ximian Inc. |
---|
3 | |
---|
4 | Author: Michael Zucchi <notzed@ximian.com> |
---|
5 | |
---|
6 | code for managing vfolders |
---|
7 | |
---|
8 | NOTE: dont run this through fucking indent. |
---|
9 | */ |
---|
10 | |
---|
11 | #ifdef HAVE_CONFIG_H |
---|
12 | #include <config.h> |
---|
13 | #endif |
---|
14 | |
---|
15 | #include <glib.h> |
---|
16 | #include <libgnome/gnome-defs.h> |
---|
17 | #include <libgnome/gnome-i18n.h> |
---|
18 | #include <libgnomeui/gnome-dialog.h> |
---|
19 | #include <libgnomeui/gnome-dialog-util.h> |
---|
20 | #include <libgnomeui/gnome-stock.h> |
---|
21 | |
---|
22 | #include "Evolution.h" |
---|
23 | #include "evolution-storage.h" |
---|
24 | |
---|
25 | #include "evolution-shell-component.h" |
---|
26 | #include "folder-browser.h" |
---|
27 | #include "mail-vfolder.h" |
---|
28 | #include "mail-tools.h" |
---|
29 | #include "mail-autofilter.h" |
---|
30 | #include "mail-folder-cache.h" |
---|
31 | #include "mail.h" |
---|
32 | #include "mail-ops.h" |
---|
33 | #include "mail-mt.h" |
---|
34 | |
---|
35 | #include "gal/widgets/e-gui-utils.h" |
---|
36 | #include "gal/util/e-unicode-i18n.h" |
---|
37 | |
---|
38 | #include "camel/camel.h" |
---|
39 | #include "camel/camel-remote-store.h" |
---|
40 | #include "camel/camel-vee-folder.h" |
---|
41 | #include "camel/camel-vee-store.h" |
---|
42 | |
---|
43 | #include "filter/vfolder-context.h" |
---|
44 | #include "filter/vfolder-editor.h" |
---|
45 | |
---|
46 | #define d(x) |
---|
47 | |
---|
48 | static VfolderContext *context; /* context remains open all time */ |
---|
49 | static CamelStore *vfolder_store; /* the 1 static vfolder store */ |
---|
50 | |
---|
51 | /* lock for accessing shared resources (below) */ |
---|
52 | static pthread_mutex_t vfolder_lock = PTHREAD_MUTEX_INITIALIZER; |
---|
53 | |
---|
54 | static GList *source_folders_remote; /* list of source folder uri's - remote ones */ |
---|
55 | static GList *source_folders_local; /* list of source folder uri's - local ones */ |
---|
56 | static GHashTable *vfolder_hash; |
---|
57 | |
---|
58 | extern EvolutionShellClient *global_shell_client; |
---|
59 | |
---|
60 | /* more globals ... */ |
---|
61 | extern char *evolution_dir; |
---|
62 | extern CamelSession *session; |
---|
63 | |
---|
64 | static void rule_changed(FilterRule *rule, CamelFolder *folder); |
---|
65 | |
---|
66 | #define LOCK() pthread_mutex_lock(&vfolder_lock); |
---|
67 | #define UNLOCK() pthread_mutex_unlock(&vfolder_lock); |
---|
68 | |
---|
69 | /* ********************************************************************** */ |
---|
70 | |
---|
71 | struct _setup_msg { |
---|
72 | struct _mail_msg msg; |
---|
73 | |
---|
74 | CamelFolder *folder; |
---|
75 | char *query; |
---|
76 | GList *sources_uri; |
---|
77 | GList *sources_folder; |
---|
78 | }; |
---|
79 | |
---|
80 | static char * |
---|
81 | vfolder_setup_desc(struct _mail_msg *mm, int done) |
---|
82 | { |
---|
83 | struct _setup_msg *m = (struct _setup_msg *)mm; |
---|
84 | |
---|
85 | return g_strdup_printf(_("Setting up vfolder: %s"), m->folder->full_name); |
---|
86 | } |
---|
87 | |
---|
88 | static void |
---|
89 | vfolder_setup_do(struct _mail_msg *mm) |
---|
90 | { |
---|
91 | struct _setup_msg *m = (struct _setup_msg *)mm; |
---|
92 | GList *l, *list = NULL; |
---|
93 | CamelFolder *folder; |
---|
94 | |
---|
95 | d(printf("Setting up vfolder: %s\n", m->folder->full_name)); |
---|
96 | |
---|
97 | camel_vee_folder_set_expression((CamelVeeFolder *)m->folder, m->query); |
---|
98 | |
---|
99 | l = m->sources_uri; |
---|
100 | while (l) { |
---|
101 | d(printf(" Adding uri: %s\n", (char *)l->data)); |
---|
102 | folder = mail_tool_uri_to_folder (l->data, 0, &mm->ex); |
---|
103 | if (folder) { |
---|
104 | list = g_list_append(list, folder); |
---|
105 | } else { |
---|
106 | g_warning("Could not open vfolder source: %s", (char *)l->data); |
---|
107 | camel_exception_clear(&mm->ex); |
---|
108 | } |
---|
109 | l = l->next; |
---|
110 | } |
---|
111 | |
---|
112 | l = m->sources_folder; |
---|
113 | while (l) { |
---|
114 | d(printf(" Adding folder: %s\n", ((CamelFolder *)l->data)->full_name)); |
---|
115 | camel_object_ref((CamelObject *)l->data); |
---|
116 | list = g_list_append(list, l->data); |
---|
117 | l = l->next; |
---|
118 | } |
---|
119 | |
---|
120 | camel_vee_folder_set_folders((CamelVeeFolder *)m->folder, list); |
---|
121 | |
---|
122 | l = list; |
---|
123 | while (l) { |
---|
124 | camel_object_unref((CamelObject *)l->data); |
---|
125 | l = l->next; |
---|
126 | } |
---|
127 | g_list_free(list); |
---|
128 | } |
---|
129 | |
---|
130 | static void |
---|
131 | vfolder_setup_done(struct _mail_msg *mm) |
---|
132 | { |
---|
133 | struct _setup_msg *m = (struct _setup_msg *)mm; |
---|
134 | |
---|
135 | m = m; |
---|
136 | } |
---|
137 | |
---|
138 | static void |
---|
139 | vfolder_setup_free (struct _mail_msg *mm) |
---|
140 | { |
---|
141 | struct _setup_msg *m = (struct _setup_msg *)mm; |
---|
142 | GList *l; |
---|
143 | |
---|
144 | camel_object_unref((CamelObject *)m->folder); |
---|
145 | g_free(m->query); |
---|
146 | |
---|
147 | l = m->sources_uri; |
---|
148 | while (l) { |
---|
149 | g_free(l->data); |
---|
150 | l = l->next; |
---|
151 | } |
---|
152 | g_list_free(m->sources_uri); |
---|
153 | |
---|
154 | l = m->sources_folder; |
---|
155 | while (l) { |
---|
156 | camel_object_unref(l->data); |
---|
157 | l = l->next; |
---|
158 | } |
---|
159 | g_list_free(m->sources_folder); |
---|
160 | } |
---|
161 | |
---|
162 | static struct _mail_msg_op vfolder_setup_op = { |
---|
163 | vfolder_setup_desc, |
---|
164 | vfolder_setup_do, |
---|
165 | vfolder_setup_done, |
---|
166 | vfolder_setup_free, |
---|
167 | }; |
---|
168 | |
---|
169 | static int |
---|
170 | vfolder_setup(CamelFolder *folder, const char *query, GList *sources_uri, GList *sources_folder) |
---|
171 | { |
---|
172 | struct _setup_msg *m; |
---|
173 | int id; |
---|
174 | |
---|
175 | m = mail_msg_new(&vfolder_setup_op, NULL, sizeof (*m)); |
---|
176 | m->folder = folder; |
---|
177 | camel_object_ref((CamelObject *)folder); |
---|
178 | m->query = g_strdup(query); |
---|
179 | m->sources_uri = sources_uri; |
---|
180 | m->sources_folder = sources_folder; |
---|
181 | |
---|
182 | id = m->msg.seq; |
---|
183 | e_thread_put(mail_thread_queued_slow, (EMsg *)m); |
---|
184 | |
---|
185 | return id; |
---|
186 | } |
---|
187 | |
---|
188 | /* ********************************************************************** */ |
---|
189 | |
---|
190 | struct _adduri_msg { |
---|
191 | struct _mail_msg msg; |
---|
192 | |
---|
193 | char *uri; |
---|
194 | GList *folders; |
---|
195 | int remove; |
---|
196 | }; |
---|
197 | |
---|
198 | static char * |
---|
199 | vfolder_adduri_desc(struct _mail_msg *mm, int done) |
---|
200 | { |
---|
201 | struct _adduri_msg *m = (struct _adduri_msg *)mm; |
---|
202 | |
---|
203 | return g_strdup_printf(_("Updating vfolders for uri: %s"), m->uri); |
---|
204 | } |
---|
205 | |
---|
206 | static void |
---|
207 | vfolder_adduri_do(struct _mail_msg *mm) |
---|
208 | { |
---|
209 | struct _adduri_msg *m = (struct _adduri_msg *)mm; |
---|
210 | GList *l; |
---|
211 | CamelFolder *folder = NULL; |
---|
212 | extern CamelFolder *drafts_folder, *outbox_folder, *sent_folder; |
---|
213 | |
---|
214 | d(printf("%s uri to vfolder: %s\n", m->remove?"Removing":"Adding", m->uri)); |
---|
215 | |
---|
216 | /* we dont try lookup the cache if we are removing it, its no longer there */ |
---|
217 | if (!m->remove && !mail_note_get_folder_from_uri(m->uri, &folder)) { |
---|
218 | g_warning("Folder '%s' disappeared while I was adding/remove it to/from my vfolder", m->uri); |
---|
219 | return; |
---|
220 | } |
---|
221 | |
---|
222 | if (folder == NULL) |
---|
223 | folder = mail_tool_uri_to_folder (m->uri, 0, &mm->ex); |
---|
224 | |
---|
225 | if (folder != NULL) { |
---|
226 | if (folder != drafts_folder && folder != outbox_folder && folder != sent_folder) { |
---|
227 | l = m->folders; |
---|
228 | while (l) { |
---|
229 | if (m->remove) |
---|
230 | camel_vee_folder_remove_folder((CamelVeeFolder *)l->data, folder); |
---|
231 | else |
---|
232 | camel_vee_folder_add_folder((CamelVeeFolder *)l->data, folder); |
---|
233 | l = l->next; |
---|
234 | } |
---|
235 | } |
---|
236 | camel_object_unref((CamelObject *)folder); |
---|
237 | } |
---|
238 | } |
---|
239 | |
---|
240 | static void |
---|
241 | vfolder_adduri_done(struct _mail_msg *mm) |
---|
242 | { |
---|
243 | struct _adduri_msg *m = (struct _adduri_msg *)mm; |
---|
244 | |
---|
245 | m = m; |
---|
246 | } |
---|
247 | |
---|
248 | static void |
---|
249 | vfolder_adduri_free (struct _mail_msg *mm) |
---|
250 | { |
---|
251 | struct _adduri_msg *m = (struct _adduri_msg *)mm; |
---|
252 | |
---|
253 | g_list_foreach(m->folders, (GFunc)camel_object_unref, NULL); |
---|
254 | g_list_free(m->folders); |
---|
255 | g_free(m->uri); |
---|
256 | } |
---|
257 | |
---|
258 | static struct _mail_msg_op vfolder_adduri_op = { |
---|
259 | vfolder_adduri_desc, |
---|
260 | vfolder_adduri_do, |
---|
261 | vfolder_adduri_done, |
---|
262 | vfolder_adduri_free, |
---|
263 | }; |
---|
264 | |
---|
265 | static int |
---|
266 | vfolder_adduri(const char *uri, GList *folders, int remove) |
---|
267 | { |
---|
268 | struct _adduri_msg *m; |
---|
269 | int id; |
---|
270 | |
---|
271 | m = mail_msg_new(&vfolder_adduri_op, NULL, sizeof (*m)); |
---|
272 | m->folders = folders; |
---|
273 | m->uri = g_strdup(uri); |
---|
274 | m->remove = remove; |
---|
275 | |
---|
276 | id = m->msg.seq; |
---|
277 | e_thread_put(mail_thread_queued_slow, (EMsg *)m); |
---|
278 | |
---|
279 | return id; |
---|
280 | } |
---|
281 | |
---|
282 | /* ********************************************************************** */ |
---|
283 | |
---|
284 | /* So, uh, apparently g_list_find_custom expect the compare func to return 0 to mean true? */ |
---|
285 | static GList * |
---|
286 | my_list_find(GList *l, const char *uri, GCompareFunc cmp) |
---|
287 | { |
---|
288 | while (l) { |
---|
289 | if (cmp(l->data, uri)) |
---|
290 | break; |
---|
291 | l = l->next; |
---|
292 | } |
---|
293 | return l; |
---|
294 | } |
---|
295 | |
---|
296 | /* called when a new uri becomes (un)available */ |
---|
297 | void |
---|
298 | mail_vfolder_add_uri(CamelStore *store, const char *uri, int remove) |
---|
299 | { |
---|
300 | FilterRule *rule; |
---|
301 | const char *source; |
---|
302 | CamelVeeFolder *vf; |
---|
303 | GList *folders = NULL, *link; |
---|
304 | int remote = (((CamelService *)store)->provider->flags & CAMEL_PROVIDER_IS_REMOTE) != 0; |
---|
305 | GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; |
---|
306 | |
---|
307 | if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || context == NULL) |
---|
308 | return; |
---|
309 | |
---|
310 | g_assert(pthread_self() == mail_gui_thread); |
---|
311 | |
---|
312 | LOCK(); |
---|
313 | |
---|
314 | d(printf("%s uri to check: %s\n", remove?"Removing":"Adding", uri)); |
---|
315 | |
---|
316 | /* maintain the source folders lists for changed rules later on */ |
---|
317 | if (remove) { |
---|
318 | if (remote) { |
---|
319 | if ((link = my_list_find(source_folders_remote, (void *)uri, uri_cmp)) != NULL) { |
---|
320 | g_free(link->data); |
---|
321 | source_folders_remote = g_list_remove_link(source_folders_remote, link); |
---|
322 | } |
---|
323 | } else { |
---|
324 | if ((link = my_list_find(source_folders_local, (void *)uri, uri_cmp)) != NULL) { |
---|
325 | g_free(link->data); |
---|
326 | source_folders_local = g_list_remove_link(source_folders_local, link); |
---|
327 | } |
---|
328 | } |
---|
329 | } else { |
---|
330 | if (remote) { |
---|
331 | if (my_list_find(source_folders_remote, (void *)uri, uri_cmp) == NULL) |
---|
332 | source_folders_remote = g_list_prepend(source_folders_remote, g_strdup(uri)); |
---|
333 | } else { |
---|
334 | if (my_list_find(source_folders_local, (void *)uri, uri_cmp) == NULL) |
---|
335 | source_folders_local = g_list_prepend(source_folders_local, g_strdup(uri)); |
---|
336 | } |
---|
337 | } |
---|
338 | |
---|
339 | rule = NULL; |
---|
340 | while ((rule = rule_context_next_rule((RuleContext *)context, rule, NULL))) { |
---|
341 | int found = FALSE; |
---|
342 | |
---|
343 | if (!rule->name) { |
---|
344 | d(printf ("invalid rule (%p): rule->name is set to NULL\n")); |
---|
345 | continue; |
---|
346 | } |
---|
347 | |
---|
348 | if (rule->source |
---|
349 | && ((!strcmp(rule->source, "local") && !remote) |
---|
350 | || (!strcmp(rule->source, "remote_active") && remote) |
---|
351 | || (!strcmp(rule->source, "local_remote_active")))) |
---|
352 | found = TRUE; |
---|
353 | |
---|
354 | /* we check using the store uri_cmp since its more accurate */ |
---|
355 | source = NULL; |
---|
356 | while (!found && (source = vfolder_rule_next_source((VfolderRule *)rule, source))) |
---|
357 | found = uri_cmp(uri, source); |
---|
358 | |
---|
359 | if (found) { |
---|
360 | vf = g_hash_table_lookup(vfolder_hash, rule->name); |
---|
361 | g_assert(vf); |
---|
362 | camel_object_ref((CamelObject *)vf); |
---|
363 | folders = g_list_prepend(folders, vf); |
---|
364 | } |
---|
365 | } |
---|
366 | |
---|
367 | UNLOCK(); |
---|
368 | |
---|
369 | if (folders != NULL) |
---|
370 | vfolder_adduri(uri, folders, remove); |
---|
371 | } |
---|
372 | |
---|
373 | /* called when a uri is deleted from a store */ |
---|
374 | void |
---|
375 | mail_vfolder_delete_uri(CamelStore *store, const char *uri) |
---|
376 | { |
---|
377 | GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; |
---|
378 | FilterRule *rule; |
---|
379 | const char *source; |
---|
380 | CamelVeeFolder *vf; |
---|
381 | GString *changed; |
---|
382 | |
---|
383 | if (context == NULL || !strncmp(uri, "vtrash:", 7)) |
---|
384 | return; |
---|
385 | |
---|
386 | d(printf("Deleting uri to check: %s\n", uri)); |
---|
387 | |
---|
388 | g_assert(pthread_self() == mail_gui_thread); |
---|
389 | |
---|
390 | changed = g_string_new(""); |
---|
391 | |
---|
392 | LOCK(); |
---|
393 | |
---|
394 | /* see if any rules directly reference this removed uri */ |
---|
395 | rule = NULL; |
---|
396 | while ( (rule = rule_context_next_rule((RuleContext *)context, rule, NULL)) ) { |
---|
397 | source = NULL; |
---|
398 | while ( (source = vfolder_rule_next_source((VfolderRule *)rule, source)) ) { |
---|
399 | /* Remove all sources that match, ignore changed events though |
---|
400 | because the adduri call above does the work async */ |
---|
401 | if (uri_cmp(uri, source)) { |
---|
402 | vf = g_hash_table_lookup(vfolder_hash, rule->name); |
---|
403 | g_assert(vf); |
---|
404 | gtk_signal_disconnect_by_func((GtkObject *)rule, rule_changed, vf); |
---|
405 | vfolder_rule_remove_source((VfolderRule *)rule, source); |
---|
406 | gtk_signal_connect((GtkObject *)rule, "changed", rule_changed, vf); |
---|
407 | g_string_sprintfa(changed, " %s\n", rule->name); |
---|
408 | source = NULL; |
---|
409 | } |
---|
410 | } |
---|
411 | } |
---|
412 | |
---|
413 | UNLOCK(); |
---|
414 | |
---|
415 | if (changed->str[0]) { |
---|
416 | GnomeDialog *gd; |
---|
417 | char *text, *user; |
---|
418 | |
---|
419 | text = g_strdup_printf(_("The following vFolder(s):\n%s" |
---|
420 | "Used the removed folder:\n '%s'\n" |
---|
421 | "And have been updated."), |
---|
422 | changed->str, uri); |
---|
423 | |
---|
424 | gd = (GnomeDialog *)gnome_warning_dialog(text); |
---|
425 | g_free(text); |
---|
426 | gnome_dialog_set_close(gd, TRUE); |
---|
427 | gtk_widget_show((GtkWidget *)gd); |
---|
428 | |
---|
429 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
430 | rule_context_save((RuleContext *)context, user); |
---|
431 | g_free(user); |
---|
432 | } |
---|
433 | |
---|
434 | g_string_free(changed, TRUE); |
---|
435 | } |
---|
436 | |
---|
437 | /* called when a uri is renamed in a store */ |
---|
438 | void |
---|
439 | mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to) |
---|
440 | { |
---|
441 | GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; |
---|
442 | FilterRule *rule; |
---|
443 | const char *source; |
---|
444 | CamelVeeFolder *vf; |
---|
445 | int changed = 0; |
---|
446 | |
---|
447 | d(printf("vfolder rename uri: %s to %s\n", from, to)); |
---|
448 | |
---|
449 | if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7)) |
---|
450 | return; |
---|
451 | |
---|
452 | g_assert(pthread_self() == mail_gui_thread); |
---|
453 | |
---|
454 | LOCK(); |
---|
455 | |
---|
456 | /* see if any rules directly reference this removed uri */ |
---|
457 | rule = NULL; |
---|
458 | while ( (rule = rule_context_next_rule((RuleContext *)context, rule, NULL)) ) { |
---|
459 | source = NULL; |
---|
460 | while ( (source = vfolder_rule_next_source((VfolderRule *)rule, source)) ) { |
---|
461 | /* Remove all sources that match, ignore changed events though |
---|
462 | because the adduri call above does the work async */ |
---|
463 | if (uri_cmp(from, source)) { |
---|
464 | d(printf("Vfolder '%s' used '%s' ('%s') now uses '%s'\n", rule->name, source, from, to)); |
---|
465 | vf = g_hash_table_lookup(vfolder_hash, rule->name); |
---|
466 | g_assert(vf); |
---|
467 | gtk_signal_disconnect_by_func((GtkObject *)rule, rule_changed, vf); |
---|
468 | vfolder_rule_remove_source((VfolderRule *)rule, source); |
---|
469 | vfolder_rule_add_source((VfolderRule *)rule, to); |
---|
470 | gtk_signal_connect((GtkObject *)rule, "changed", rule_changed, vf); |
---|
471 | changed++; |
---|
472 | source = NULL; |
---|
473 | } |
---|
474 | } |
---|
475 | } |
---|
476 | |
---|
477 | UNLOCK(); |
---|
478 | |
---|
479 | if (changed) { |
---|
480 | char *user; |
---|
481 | |
---|
482 | d(printf("Vfolders updated from renamed folder\n")); |
---|
483 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
484 | rule_context_save((RuleContext *)context, user); |
---|
485 | g_free(user); |
---|
486 | } |
---|
487 | } |
---|
488 | |
---|
489 | /* ********************************************************************** */ |
---|
490 | |
---|
491 | static void context_rule_added(RuleContext *ctx, FilterRule *rule); |
---|
492 | |
---|
493 | static void |
---|
494 | rule_changed(FilterRule *rule, CamelFolder *folder) |
---|
495 | { |
---|
496 | const char *sourceuri; |
---|
497 | GList *l; |
---|
498 | GList *sources_uri = NULL, *sources_folder = NULL; |
---|
499 | GString *query; |
---|
500 | int i; |
---|
501 | CamelFolder *newfolder; |
---|
502 | |
---|
503 | /* if the folder has changed name, then add it, then remove the old manually */ |
---|
504 | if (strcmp(folder->full_name, rule->name) != 0) { |
---|
505 | char *key, *oldname; |
---|
506 | CamelFolder *old; |
---|
507 | |
---|
508 | LOCK(); |
---|
509 | d(printf("Changing folder name in hash table to '%s'\n", rule->name)); |
---|
510 | if (g_hash_table_lookup_extended(vfolder_hash, folder->full_name, (void **)&key, (void **)&old)) { |
---|
511 | g_hash_table_remove(vfolder_hash, key); |
---|
512 | g_free(key); |
---|
513 | g_hash_table_insert(vfolder_hash, g_strdup(rule->name), folder); |
---|
514 | UNLOCK(); |
---|
515 | } else { |
---|
516 | UNLOCK(); |
---|
517 | g_warning("couldn't find a vfolder rule in our table? %s", folder->full_name); |
---|
518 | } |
---|
519 | |
---|
520 | /* TODO: make the folder->full_name var thread accessible */ |
---|
521 | oldname = g_strdup(folder->full_name); |
---|
522 | camel_store_rename_folder(vfolder_store, oldname, rule->name, NULL); |
---|
523 | g_free(oldname); |
---|
524 | } |
---|
525 | |
---|
526 | d(printf("Filter rule changed? for folder '%s'!!\n", folder->name)); |
---|
527 | |
---|
528 | /* find any (currently available) folders, and add them to the ones to open */ |
---|
529 | sourceuri = NULL; |
---|
530 | while ( (sourceuri = vfolder_rule_next_source((VfolderRule *)rule, sourceuri)) ) { |
---|
531 | if (mail_note_get_folder_from_uri(sourceuri, &newfolder)) { |
---|
532 | if (newfolder) |
---|
533 | sources_folder = g_list_append(sources_folder, newfolder); |
---|
534 | else |
---|
535 | sources_uri = g_list_append(sources_uri, g_strdup(sourceuri)); |
---|
536 | } |
---|
537 | } |
---|
538 | |
---|
539 | /* check the remote/local uri lists for any other uri's that should be looked at */ |
---|
540 | if (rule->source) { |
---|
541 | LOCK(); |
---|
542 | for (i=0;i<2;i++) { |
---|
543 | if (i==0 && (!strcmp(rule->source, "local") || !strcmp(rule->source, "local_remote_active"))) |
---|
544 | l = source_folders_local; |
---|
545 | else if (i==1 && (!strcmp(rule->source, "remote_active") || !strcmp(rule->source, "local_remote_active"))) |
---|
546 | l = source_folders_remote; |
---|
547 | else |
---|
548 | l = NULL; |
---|
549 | |
---|
550 | while (l) { |
---|
551 | if (mail_note_get_folder_from_uri(l->data, &newfolder)) { |
---|
552 | if (newfolder) |
---|
553 | sources_folder = g_list_append(sources_folder, newfolder); |
---|
554 | else |
---|
555 | sources_uri = g_list_append(sources_uri, g_strdup(l->data)); |
---|
556 | } else { |
---|
557 | d(printf(" -> No such folder?\n")); |
---|
558 | } |
---|
559 | l = l->next; |
---|
560 | } |
---|
561 | } |
---|
562 | UNLOCK(); |
---|
563 | } |
---|
564 | |
---|
565 | query = g_string_new(""); |
---|
566 | filter_rule_build_code(rule, query); |
---|
567 | |
---|
568 | vfolder_setup(folder, query->str, sources_uri, sources_folder); |
---|
569 | |
---|
570 | g_string_free(query, TRUE); |
---|
571 | } |
---|
572 | |
---|
573 | static void context_rule_added(RuleContext *ctx, FilterRule *rule) |
---|
574 | { |
---|
575 | CamelFolder *folder; |
---|
576 | |
---|
577 | d(printf("rule added: %s\n", rule->name)); |
---|
578 | |
---|
579 | /* this always runs quickly */ |
---|
580 | folder = camel_store_get_folder(vfolder_store, rule->name, 0, NULL); |
---|
581 | if (folder) { |
---|
582 | gtk_signal_connect((GtkObject *)rule, "changed", rule_changed, folder); |
---|
583 | |
---|
584 | LOCK(); |
---|
585 | g_hash_table_insert(vfolder_hash, g_strdup(rule->name), folder); |
---|
586 | UNLOCK(); |
---|
587 | |
---|
588 | mail_note_folder(folder); |
---|
589 | rule_changed(rule, folder); |
---|
590 | } |
---|
591 | } |
---|
592 | |
---|
593 | static void context_rule_removed(RuleContext *ctx, FilterRule *rule) |
---|
594 | { |
---|
595 | char *key, *path; |
---|
596 | CamelFolder *folder; |
---|
597 | |
---|
598 | d(printf("rule removed; %s\n", rule->name)); |
---|
599 | |
---|
600 | /* TODO: remove from folder info cache? */ |
---|
601 | |
---|
602 | path = g_strdup_printf("/%s", rule->name); |
---|
603 | evolution_storage_removed_folder(mail_lookup_storage(vfolder_store), path); |
---|
604 | g_free(path); |
---|
605 | |
---|
606 | LOCK(); |
---|
607 | if (g_hash_table_lookup_extended(vfolder_hash, rule->name, (void **)&key, (void **)&folder)) { |
---|
608 | g_hash_table_remove(vfolder_hash, key); |
---|
609 | g_free(key); |
---|
610 | UNLOCK(); |
---|
611 | camel_object_unref((CamelObject *)folder); |
---|
612 | } else |
---|
613 | UNLOCK(); |
---|
614 | |
---|
615 | camel_store_delete_folder(vfolder_store, rule->name, NULL); |
---|
616 | } |
---|
617 | |
---|
618 | static void |
---|
619 | store_folder_created(CamelObject *o, void *event_data, void *data) |
---|
620 | { |
---|
621 | CamelStore *store = (CamelStore *)o; |
---|
622 | CamelFolderInfo *info = event_data; |
---|
623 | |
---|
624 | store = store; |
---|
625 | info = info; |
---|
626 | } |
---|
627 | |
---|
628 | static void |
---|
629 | store_folder_deleted(CamelObject *o, void *event_data, void *data) |
---|
630 | { |
---|
631 | CamelStore *store = (CamelStore *)o; |
---|
632 | CamelFolderInfo *info = event_data; |
---|
633 | FilterRule *rule; |
---|
634 | char *user; |
---|
635 | |
---|
636 | d(printf("Folder deleted: %s\n", info->name)); |
---|
637 | store = store; |
---|
638 | |
---|
639 | /* Warning not thread safe, but might be enough */ |
---|
640 | |
---|
641 | LOCK(); |
---|
642 | |
---|
643 | /* delete it from our list */ |
---|
644 | rule = rule_context_find_rule((RuleContext *)context, info->full_name, NULL); |
---|
645 | if (rule) { |
---|
646 | /* We need to stop listening to removed events, otherwise we'll try and remove it again */ |
---|
647 | gtk_signal_disconnect_by_func((GtkObject *)context, context_rule_removed, context); |
---|
648 | rule_context_remove_rule((RuleContext *)context, rule); |
---|
649 | gtk_object_unref((GtkObject *)rule); |
---|
650 | gtk_signal_connect((GtkObject *)context, "rule_removed", context_rule_removed, context); |
---|
651 | |
---|
652 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
653 | rule_context_save((RuleContext *)context, user); |
---|
654 | g_free(user); |
---|
655 | } else { |
---|
656 | g_warning("Cannot find rule for deleted vfolder '%s'", info->name); |
---|
657 | } |
---|
658 | |
---|
659 | UNLOCK(); |
---|
660 | } |
---|
661 | |
---|
662 | static void |
---|
663 | store_folder_renamed(CamelObject *o, void *event_data, void *data) |
---|
664 | { |
---|
665 | CamelStore *store = (CamelStore *)o; |
---|
666 | CamelRenameInfo *info = event_data; |
---|
667 | FilterRule *rule; |
---|
668 | char *user; |
---|
669 | char *key; |
---|
670 | CamelFolder *folder; |
---|
671 | |
---|
672 | store = store; |
---|
673 | |
---|
674 | /* This should be more-or-less thread-safe */ |
---|
675 | |
---|
676 | d(printf("Folder renamed to '%s' from '%s'\n", info->new->full_name, info->old_base)); |
---|
677 | |
---|
678 | /* Folder is already renamed? */ |
---|
679 | LOCK(); |
---|
680 | d(printf("Changing folder name in hash table to '%s'\n", info->new->full_name)); |
---|
681 | if (g_hash_table_lookup_extended(vfolder_hash, info->old_base, (void **)&key, (void **)&folder)) { |
---|
682 | g_hash_table_remove(vfolder_hash, key); |
---|
683 | g_free(key); |
---|
684 | g_hash_table_insert(vfolder_hash, g_strdup(info->new->full_name), folder); |
---|
685 | |
---|
686 | rule = rule_context_find_rule((RuleContext *)context, info->old_base, NULL); |
---|
687 | g_assert(rule); |
---|
688 | |
---|
689 | gtk_signal_disconnect_by_func((GtkObject *)rule, rule_changed, folder); |
---|
690 | filter_rule_set_name(rule, info->new->name); |
---|
691 | gtk_signal_connect((GtkObject *)rule, "changed", rule_changed, folder); |
---|
692 | |
---|
693 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
694 | rule_context_save((RuleContext *)context, user); |
---|
695 | g_free(user); |
---|
696 | |
---|
697 | UNLOCK(); |
---|
698 | } else { |
---|
699 | UNLOCK(); |
---|
700 | g_warning("couldn't find a vfolder rule in our table? %s", info->new->full_name); |
---|
701 | } |
---|
702 | } |
---|
703 | |
---|
704 | void |
---|
705 | vfolder_load_storage(GNOME_Evolution_Shell shell) |
---|
706 | { |
---|
707 | char *user, *storeuri; |
---|
708 | FilterRule *rule; |
---|
709 | |
---|
710 | vfolder_hash = g_hash_table_new(g_str_hash, g_str_equal); |
---|
711 | |
---|
712 | /* first, create the vfolder store, and set it up */ |
---|
713 | storeuri = g_strdup_printf("vfolder:%s/vfolder", evolution_dir); |
---|
714 | vfolder_store = camel_session_get_store(session, storeuri, NULL); |
---|
715 | if (vfolder_store == NULL) { |
---|
716 | g_warning("Cannot open vfolder store - no vfolders available"); |
---|
717 | return; |
---|
718 | } |
---|
719 | |
---|
720 | camel_object_hook_event((CamelObject *)vfolder_store, "folder_created", |
---|
721 | (CamelObjectEventHookFunc)store_folder_created, NULL); |
---|
722 | camel_object_hook_event((CamelObject *)vfolder_store, "folder_deleted", |
---|
723 | (CamelObjectEventHookFunc)store_folder_deleted, NULL); |
---|
724 | camel_object_hook_event((CamelObject *)vfolder_store, "folder_renamed", |
---|
725 | (CamelObjectEventHookFunc)store_folder_renamed, NULL); |
---|
726 | |
---|
727 | d(printf("got store '%s' = %p\n", storeuri, vfolder_store)); |
---|
728 | mail_load_storage_by_uri(shell, storeuri, U_("VFolders")); |
---|
729 | |
---|
730 | /* load our rules */ |
---|
731 | user = g_strdup_printf ("%s/vfolders.xml", evolution_dir); |
---|
732 | context = vfolder_context_new (); |
---|
733 | if (rule_context_load ((RuleContext *)context, EVOLUTION_DATADIR "/evolution/vfoldertypes.xml", user) != 0) { |
---|
734 | g_warning("cannot load vfolders: %s\n", ((RuleContext *)context)->error); |
---|
735 | } |
---|
736 | g_free (user); |
---|
737 | |
---|
738 | gtk_signal_connect((GtkObject *)context, "rule_added", context_rule_added, context); |
---|
739 | gtk_signal_connect((GtkObject *)context, "rule_removed", context_rule_removed, context); |
---|
740 | |
---|
741 | /* and setup the rules we have */ |
---|
742 | rule = NULL; |
---|
743 | while ( (rule = rule_context_next_rule((RuleContext *)context, rule, NULL)) ) { |
---|
744 | if (rule->name) |
---|
745 | context_rule_added((RuleContext *)context, rule); |
---|
746 | else |
---|
747 | d(printf ("invalid rule (%p) encountered: rule->name is NULL\n")); |
---|
748 | } |
---|
749 | |
---|
750 | g_free(storeuri); |
---|
751 | } |
---|
752 | |
---|
753 | static GtkWidget *vfolder_editor = NULL; |
---|
754 | |
---|
755 | static void |
---|
756 | vfolder_editor_destroy (GtkWidget *widget, gpointer user_data) |
---|
757 | { |
---|
758 | vfolder_editor = NULL; |
---|
759 | } |
---|
760 | |
---|
761 | static void |
---|
762 | vfolder_editor_clicked (GtkWidget *dialog, int button, void *data) |
---|
763 | { |
---|
764 | if (button == 0) { |
---|
765 | char *user; |
---|
766 | |
---|
767 | user = g_strdup_printf ("%s/vfolders.xml", evolution_dir); |
---|
768 | rule_context_save ((RuleContext *)context, user); |
---|
769 | g_free (user); |
---|
770 | } |
---|
771 | if (button != -1) { |
---|
772 | gnome_dialog_close (GNOME_DIALOG (dialog)); |
---|
773 | } |
---|
774 | } |
---|
775 | |
---|
776 | void |
---|
777 | vfolder_edit (void) |
---|
778 | { |
---|
779 | if (vfolder_editor) { |
---|
780 | gdk_window_raise (GTK_WIDGET (vfolder_editor)->window); |
---|
781 | return; |
---|
782 | } |
---|
783 | |
---|
784 | vfolder_editor = GTK_WIDGET (vfolder_editor_new (context)); |
---|
785 | gtk_window_set_title (GTK_WINDOW (vfolder_editor), _("vFolders")); |
---|
786 | gtk_signal_connect (GTK_OBJECT (vfolder_editor), "clicked", vfolder_editor_clicked, NULL); |
---|
787 | gtk_signal_connect (GTK_OBJECT (vfolder_editor), "destroy", vfolder_editor_destroy, NULL); |
---|
788 | gtk_widget_show (vfolder_editor); |
---|
789 | } |
---|
790 | |
---|
791 | static void |
---|
792 | edit_rule_clicked(GtkWidget *w, int button, void *data) |
---|
793 | { |
---|
794 | if (button == 0) { |
---|
795 | char *user; |
---|
796 | FilterRule *rule = gtk_object_get_data((GtkObject *)w, "rule"); |
---|
797 | FilterRule *orig = gtk_object_get_data((GtkObject *)w, "orig"); |
---|
798 | |
---|
799 | filter_rule_copy(orig, rule); |
---|
800 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
801 | rule_context_save((RuleContext *)context, user); |
---|
802 | g_free(user); |
---|
803 | } |
---|
804 | if (button != -1) { |
---|
805 | gnome_dialog_close((GnomeDialog *)w); |
---|
806 | } |
---|
807 | } |
---|
808 | |
---|
809 | void |
---|
810 | vfolder_edit_rule(const char *uri) |
---|
811 | { |
---|
812 | GtkWidget *w; |
---|
813 | GnomeDialog *gd; |
---|
814 | FilterRule *rule, *newrule; |
---|
815 | CamelURL *url; |
---|
816 | |
---|
817 | url = camel_url_new(uri, NULL); |
---|
818 | if (url && url->fragment |
---|
819 | && (rule = rule_context_find_rule((RuleContext *)context, url->fragment, NULL))) { |
---|
820 | gtk_object_ref((GtkObject *)rule); |
---|
821 | newrule = filter_rule_clone(rule); |
---|
822 | |
---|
823 | w = filter_rule_get_widget((FilterRule *)newrule, (RuleContext *)context); |
---|
824 | |
---|
825 | gd = (GnomeDialog *)gnome_dialog_new(_("Edit VFolder"), |
---|
826 | GNOME_STOCK_BUTTON_OK, |
---|
827 | GNOME_STOCK_BUTTON_CANCEL, |
---|
828 | NULL); |
---|
829 | gnome_dialog_set_default (gd, 0); |
---|
830 | |
---|
831 | gtk_window_set_policy(GTK_WINDOW(gd), FALSE, TRUE, FALSE); |
---|
832 | gtk_window_set_default_size (GTK_WINDOW (gd), 500, 500); |
---|
833 | gtk_box_pack_start((GtkBox *)gd->vbox, w, TRUE, TRUE, 0); |
---|
834 | gtk_widget_show((GtkWidget *)gd); |
---|
835 | gtk_object_set_data_full((GtkObject *)gd, "rule", newrule, (GtkDestroyNotify)gtk_object_unref); |
---|
836 | gtk_object_set_data_full((GtkObject *)gd, "orig", rule, (GtkDestroyNotify)gtk_object_unref); |
---|
837 | gtk_signal_connect((GtkObject *)gd, "clicked", edit_rule_clicked, NULL); |
---|
838 | gtk_widget_show((GtkWidget *)gd); |
---|
839 | } else { |
---|
840 | e_notice (NULL, GNOME_MESSAGE_BOX_WARNING, |
---|
841 | _("Trying to edit a vfolder '%s' which doesn't exist."), uri); |
---|
842 | } |
---|
843 | |
---|
844 | if (url) |
---|
845 | camel_url_free(url); |
---|
846 | } |
---|
847 | |
---|
848 | static void |
---|
849 | new_rule_clicked(GtkWidget *w, int button, void *data) |
---|
850 | { |
---|
851 | if (button == 0) { |
---|
852 | char *user; |
---|
853 | FilterRule *rule = gtk_object_get_data((GtkObject *)w, "rule"); |
---|
854 | |
---|
855 | gtk_object_ref((GtkObject *)rule); |
---|
856 | rule_context_add_rule((RuleContext *)context, rule); |
---|
857 | user = g_strdup_printf("%s/vfolders.xml", evolution_dir); |
---|
858 | rule_context_save((RuleContext *)context, user); |
---|
859 | g_free(user); |
---|
860 | } |
---|
861 | if (button != -1) { |
---|
862 | gnome_dialog_close((GnomeDialog *)w); |
---|
863 | } |
---|
864 | } |
---|
865 | |
---|
866 | FilterPart * |
---|
867 | vfolder_create_part(const char *name) |
---|
868 | { |
---|
869 | return rule_context_create_part((RuleContext *)context, name); |
---|
870 | } |
---|
871 | |
---|
872 | /* clones a filter/search rule into a matching vfolder rule (assuming the same system definitions) */ |
---|
873 | FilterRule * |
---|
874 | vfolder_clone_rule(FilterRule *in) |
---|
875 | { |
---|
876 | FilterRule *rule = (FilterRule *)vfolder_rule_new(); |
---|
877 | xmlNodePtr xml; |
---|
878 | |
---|
879 | xml = filter_rule_xml_encode(in); |
---|
880 | filter_rule_xml_decode(rule, xml, (RuleContext *)context); |
---|
881 | xmlFreeNodeList(xml); |
---|
882 | |
---|
883 | return rule; |
---|
884 | } |
---|
885 | |
---|
886 | /* adds a rule with a gui */ |
---|
887 | void |
---|
888 | vfolder_gui_add_rule(VfolderRule *rule) |
---|
889 | { |
---|
890 | GtkWidget *w; |
---|
891 | GnomeDialog *gd; |
---|
892 | |
---|
893 | w = filter_rule_get_widget((FilterRule *)rule, (RuleContext *)context); |
---|
894 | |
---|
895 | gd = (GnomeDialog *)gnome_dialog_new(_("New VFolder"), |
---|
896 | GNOME_STOCK_BUTTON_OK, |
---|
897 | GNOME_STOCK_BUTTON_CANCEL, |
---|
898 | NULL); |
---|
899 | gnome_dialog_set_default (gd, 0); |
---|
900 | |
---|
901 | gtk_window_set_policy(GTK_WINDOW(gd), FALSE, TRUE, FALSE); |
---|
902 | gtk_window_set_default_size (GTK_WINDOW (gd), 500, 500); |
---|
903 | gtk_box_pack_start((GtkBox *)gd->vbox, w, TRUE, TRUE, 0); |
---|
904 | gtk_widget_show((GtkWidget *)gd); |
---|
905 | gtk_object_set_data_full((GtkObject *)gd, "rule", rule, (GtkDestroyNotify)gtk_object_unref); |
---|
906 | gtk_signal_connect((GtkObject *)gd, "clicked", new_rule_clicked, NULL); |
---|
907 | gtk_widget_show((GtkWidget *)gd); |
---|
908 | } |
---|
909 | |
---|
910 | void |
---|
911 | vfolder_gui_add_from_message(CamelMimeMessage *msg, int flags, const char *source) |
---|
912 | { |
---|
913 | VfolderRule *rule; |
---|
914 | |
---|
915 | g_return_if_fail (msg != NULL); |
---|
916 | |
---|
917 | rule = (VfolderRule*)vfolder_rule_from_message(context, msg, flags, source); |
---|
918 | vfolder_gui_add_rule(rule); |
---|
919 | } |
---|
920 | |
---|
921 | void |
---|
922 | vfolder_gui_add_from_mlist(CamelMimeMessage *msg, const char *mlist, const char *source) |
---|
923 | { |
---|
924 | VfolderRule *rule; |
---|
925 | |
---|
926 | g_return_if_fail (msg != NULL); |
---|
927 | |
---|
928 | rule = (VfolderRule*)vfolder_rule_from_mlist(context, mlist, source); |
---|
929 | vfolder_gui_add_rule(rule); |
---|
930 | } |
---|
931 | |
---|
932 | static void |
---|
933 | vfolder_foreach_cb (gpointer key, gpointer data, gpointer user_data) |
---|
934 | { |
---|
935 | CamelFolder *folder = CAMEL_FOLDER (data); |
---|
936 | |
---|
937 | if (folder) |
---|
938 | camel_object_unref (CAMEL_OBJECT (folder)); |
---|
939 | |
---|
940 | g_free (key); |
---|
941 | } |
---|
942 | |
---|
943 | void |
---|
944 | mail_vfolder_shutdown (void) |
---|
945 | { |
---|
946 | g_hash_table_foreach (vfolder_hash, vfolder_foreach_cb, NULL); |
---|
947 | g_hash_table_destroy (vfolder_hash); |
---|
948 | vfolder_hash = NULL; |
---|
949 | |
---|
950 | if (vfolder_store) { |
---|
951 | camel_object_unref (CAMEL_OBJECT (vfolder_store)); |
---|
952 | vfolder_store = NULL; |
---|
953 | } |
---|
954 | |
---|
955 | if (context) { |
---|
956 | gtk_object_unref (GTK_OBJECT (context)); |
---|
957 | context = NULL; |
---|
958 | } |
---|
959 | } |
---|