No issues found
Tool | Failure ID | Location | Function | Message | Data |
---|---|---|---|---|---|
clang-analyzer | no-output-found | e-mail-utils.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None | |
clang-analyzer | no-output-found | e-mail-utils.c | Message(text='Unable to locate XML output from invoke-clang-analyzer') | None |
1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU Lesser General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * Lesser General Public License for more details.
11 *
12 * You should have received a copy of the GNU Lesser General Public
13 * License along with the program; if not, see <http://www.gnu.org/licenses/>
14 *
15 *
16 * Authors:
17 * Srinivasa Ragavan <sragavan@gnome.org>
18 *
19 *
20 */
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <errno.h>
27 #include <time.h>
28
29 #include <glib/gstdio.h>
30
31 #ifdef G_OS_WIN32
32 /* Work around namespace clobbage in <windows.h> */
33 #define DATADIR windows_DATADIR
34 #include <windows.h>
35 #undef DATADIR
36 #undef interface
37 #endif
38
39 #include <glib/gi18n.h>
40 #include <libebook/libebook.h>
41
42 #include <libemail-utils/mail-mt.h>
43
44 #include "e-mail-folder-utils.h"
45 #include "e-mail-session.h"
46 #include "e-mail-utils.h"
47 #include "mail-tools.h"
48
49 #define d(x)
50
51 /**
52 * em_utils_folder_is_drafts:
53 * @registry: an #ESourceRegistry
54 * @folder: a #CamelFolder
55 *
56 * Decides if @folder is a Drafts folder.
57 *
58 * Returns %TRUE if this is a Drafts folder or %FALSE otherwise.
59 **/
60 gboolean
61 em_utils_folder_is_drafts (ESourceRegistry *registry,
62 CamelFolder *folder)
63 {
64 CamelFolder *local_drafts_folder;
65 CamelSession *session;
66 CamelStore *store;
67 GList *list, *iter;
68 gchar *folder_uri;
69 gboolean is_drafts = FALSE;
70 const gchar *extension_name;
71
72 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
73
74 store = camel_folder_get_parent_store (folder);
75 session = camel_service_get_session (CAMEL_SERVICE (store));
76
77 local_drafts_folder =
78 e_mail_session_get_local_folder (
79 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_DRAFTS);
80
81 if (folder == local_drafts_folder)
82 return TRUE;
83
84 folder_uri = e_mail_folder_uri_from_folder (folder);
85
86 store = camel_folder_get_parent_store (folder);
87 session = camel_service_get_session (CAMEL_SERVICE (store));
88
89 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
90 list = e_source_registry_list_sources (registry, extension_name);
91
92 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
93 ESource *source = E_SOURCE (iter->data);
94 ESourceExtension *extension;
95 const gchar *drafts_folder_uri;
96
97 extension = e_source_get_extension (source, extension_name);
98
99 drafts_folder_uri =
100 e_source_mail_composition_get_drafts_folder (
101 E_SOURCE_MAIL_COMPOSITION (extension));
102
103 if (drafts_folder_uri != NULL)
104 is_drafts = e_mail_folder_uri_equal (
105 session, folder_uri, drafts_folder_uri);
106
107 if (is_drafts)
108 break;
109 }
110
111 g_list_free_full (list, (GDestroyNotify) g_object_unref);
112 g_free (folder_uri);
113
114 return is_drafts;
115 }
116
117 /**
118 * em_utils_folder_is_templates:
119 * @registry: an #ESourceRegistry
120 * @folder: a #CamelFolder
121 *
122 * Decides if @folder is a Templates folder.
123 *
124 * Returns %TRUE if this is a Templates folder or %FALSE otherwise.
125 **/
126
127 gboolean
128 em_utils_folder_is_templates (ESourceRegistry *registry,
129 CamelFolder *folder)
130 {
131 CamelFolder *local_templates_folder;
132 CamelSession *session;
133 CamelStore *store;
134 GList *list, *iter;
135 gchar *folder_uri;
136 gboolean is_templates = FALSE;
137 const gchar *extension_name;
138
139 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
140
141 store = camel_folder_get_parent_store (folder);
142 session = camel_service_get_session (CAMEL_SERVICE (store));
143
144 local_templates_folder =
145 e_mail_session_get_local_folder (
146 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_TEMPLATES);
147
148 if (folder == local_templates_folder)
149 return TRUE;
150
151 folder_uri = e_mail_folder_uri_from_folder (folder);
152
153 store = camel_folder_get_parent_store (folder);
154 session = camel_service_get_session (CAMEL_SERVICE (store));
155
156 extension_name = E_SOURCE_EXTENSION_MAIL_COMPOSITION;
157 list = e_source_registry_list_sources (registry, extension_name);
158
159 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
160 ESource *source = E_SOURCE (iter->data);
161 ESourceExtension *extension;
162 const gchar *templates_folder_uri;
163
164 extension = e_source_get_extension (source, extension_name);
165
166 templates_folder_uri =
167 e_source_mail_composition_get_templates_folder (
168 E_SOURCE_MAIL_COMPOSITION (extension));
169
170 if (templates_folder_uri != NULL)
171 is_templates = e_mail_folder_uri_equal (
172 session, folder_uri, templates_folder_uri);
173
174 if (is_templates)
175 break;
176 }
177
178 g_list_free_full (list, (GDestroyNotify) g_object_unref);
179 g_free (folder_uri);
180
181 return is_templates;
182 }
183
184 /**
185 * em_utils_folder_is_sent:
186 * @registry: an #ESourceRegistry
187 * @folder: a #CamelFolder
188 *
189 * Decides if @folder is a Sent folder.
190 *
191 * Returns %TRUE if this is a Sent folder or %FALSE otherwise.
192 **/
193 gboolean
194 em_utils_folder_is_sent (ESourceRegistry *registry,
195 CamelFolder *folder)
196 {
197 CamelFolder *local_sent_folder;
198 CamelSession *session;
199 CamelStore *store;
200 GList *list, *iter;
201 gchar *folder_uri;
202 gboolean is_sent = FALSE;
203 const gchar *extension_name;
204
205 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
206
207 store = camel_folder_get_parent_store (folder);
208 session = camel_service_get_session (CAMEL_SERVICE (store));
209
210 local_sent_folder =
211 e_mail_session_get_local_folder (
212 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_SENT);
213
214 if (folder == local_sent_folder)
215 return TRUE;
216
217 folder_uri = e_mail_folder_uri_from_folder (folder);
218
219 store = camel_folder_get_parent_store (folder);
220 session = camel_service_get_session (CAMEL_SERVICE (store));
221
222 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
223 list = e_source_registry_list_sources (registry, extension_name);
224
225 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
226 ESource *source = E_SOURCE (iter->data);
227 ESourceExtension *extension;
228 const gchar *sent_folder_uri;
229
230 extension = e_source_get_extension (source, extension_name);
231
232 sent_folder_uri =
233 e_source_mail_submission_get_sent_folder (
234 E_SOURCE_MAIL_SUBMISSION (extension));
235
236 if (sent_folder_uri != NULL)
237 is_sent = e_mail_folder_uri_equal (
238 session, folder_uri, sent_folder_uri);
239
240 if (is_sent)
241 break;
242 }
243
244 g_list_free_full (list, (GDestroyNotify) g_object_unref);
245 g_free (folder_uri);
246
247 return is_sent;
248 }
249
250 /**
251 * em_utils_folder_is_outbox:
252 * @registry: an #ESourceRegistry
253 * @folder: a #CamelFolder
254 *
255 * Decides if @folder is an Outbox folder.
256 *
257 * Returns %TRUE if this is an Outbox folder or %FALSE otherwise.
258 **/
259 gboolean
260 em_utils_folder_is_outbox (ESourceRegistry *registry,
261 CamelFolder *folder)
262 {
263 CamelStore *store;
264 CamelSession *session;
265 CamelFolder *local_outbox_folder;
266
267 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
268
269 store = camel_folder_get_parent_store (folder);
270 session = camel_service_get_session (CAMEL_SERVICE (store));
271
272 local_outbox_folder =
273 e_mail_session_get_local_folder (
274 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
275
276 return (folder == local_outbox_folder);
277 }
278
279 /* ********************************************************************** */
280
281 struct TryOpenEBookStruct {
282 GError **error;
283 EFlag *flag;
284 gboolean result;
285 };
286
287 static void
288 try_open_book_client_cb (GObject *source_object,
289 GAsyncResult *result,
290 gpointer closure)
291 {
292 EBookClient *book_client = E_BOOK_CLIENT (source_object);
293 struct TryOpenEBookStruct *data = (struct TryOpenEBookStruct *) closure;
294 GError *error = NULL;
295
296 if (!data)
297 return;
298
299 e_client_open_finish (E_CLIENT (book_client), result, &error);
300
301 data->result = error == NULL;
302
303 if (!data->result) {
304 g_clear_error (data->error);
305 g_propagate_error (data->error, error);
306 }
307
308 e_flag_set (data->flag);
309 }
310
311 /*
312 * try_open_book_client:
313 * Tries to open address book asynchronously, but acts as synchronous.
314 * The advantage is it checks periodically whether the camel_operation
315 * has been canceled or not, and if so, then stops immediately, with
316 * result FALSE. Otherwise returns same as e_client_open()
317 */
318 static gboolean
319 try_open_book_client (EBookClient *book_client,
320 gboolean only_if_exists,
321 GCancellable *cancellable,
322 GError **error)
323 {
324 struct TryOpenEBookStruct data;
325 gboolean canceled = FALSE;
326 EFlag *flag = e_flag_new ();
327
328 data.error = error;
329 data.flag = flag;
330 data.result = FALSE;
331
332 e_client_open (
333 E_CLIENT (book_client), only_if_exists,
334 cancellable, try_open_book_client_cb, &data);
335
336 while (canceled = g_cancellable_is_cancelled (cancellable),
337 !canceled && !e_flag_is_set (flag)) {
338 GTimeVal wait;
339
340 g_get_current_time (&wait);
341 g_time_val_add (&wait, 250000); /* waits 250ms */
342
343 e_flag_timed_wait (flag, &wait);
344 }
345
346 if (canceled) {
347 g_cancellable_cancel (cancellable);
348
349 g_clear_error (error);
350 g_propagate_error (
351 error, e_client_error_create (
352 E_CLIENT_ERROR_CANCELLED, NULL));
353 }
354
355 e_flag_wait (flag);
356 e_flag_free (flag);
357
358 return data.result && (!error || !*error);
359 }
360
361 extern gint camel_application_is_exiting;
362
363 #define NOT_FOUND_BOOK (GINT_TO_POINTER (1))
364
365 /* to be able to cancel pending requests on exit; this lock
366 * should not be held while contact_cache lock is held */
367 G_LOCK_DEFINE_STATIC (search_addressbook_cancellables);
368 static GSList *search_addressbook_cancellables = NULL;
369
370 G_LOCK_DEFINE_STATIC (contact_cache);
371
372 /* key is lowercased contact email; value is EBook pointer
373 * (just for comparison) where it comes from */
374 static GHashTable *contact_cache = NULL;
375
376 /* key is source ID; value is pointer to EBook */
377 static GHashTable *emu_books_hash = NULL;
378
379 /* key is source ID; value is same pointer as key; this is hash of
380 * broken books, which failed to open for some reason */
381 static GHashTable *emu_broken_books_hash = NULL;
382
383 static gboolean
384 search_address_in_addressbooks (ESourceRegistry *registry,
385 const gchar *address,
386 gboolean local_only,
387 gboolean (*check_contact) (EContact *contact,
388 gpointer user_data),
389 gpointer user_data,
390 GCancellable *cancellable)
391 {
392 GList *list, *link;
393 GList *addr_sources = NULL;
394 gboolean found = FALSE, stop = FALSE, found_any = FALSE;
395 gchar *lowercase_addr;
396 gpointer ptr;
397 EBookQuery *book_query;
398 gchar *query;
399 const gchar *extension_name;
400
401 if (!address || !*address || g_cancellable_is_cancelled (cancellable) || camel_application_is_exiting)
402 return FALSE;
403
404 G_LOCK (search_addressbook_cancellables);
405 if (cancellable)
406 g_object_ref (cancellable);
407 else
408 cancellable = g_cancellable_new ();
409 search_addressbook_cancellables = g_slist_prepend (search_addressbook_cancellables, cancellable);
410 G_UNLOCK (search_addressbook_cancellables);
411
412 G_LOCK (contact_cache);
413
414 if (camel_application_is_exiting || g_cancellable_is_cancelled (cancellable)) {
415 G_UNLOCK (contact_cache);
416
417 G_LOCK (search_addressbook_cancellables);
418 search_addressbook_cancellables = g_slist_remove (search_addressbook_cancellables, cancellable);
419 g_object_unref (cancellable);
420 G_UNLOCK (search_addressbook_cancellables);
421
422 return FALSE;
423 }
424
425 if (emu_books_hash == NULL) {
426 emu_books_hash = g_hash_table_new_full (
427 g_str_hash, g_str_equal, g_free, g_object_unref);
428 emu_broken_books_hash = g_hash_table_new_full (
429 g_str_hash, g_str_equal, g_free, NULL);
430 contact_cache = g_hash_table_new_full (
431 g_str_hash, g_str_equal, g_free, NULL);
432 }
433
434 lowercase_addr = g_utf8_strdown (address, -1);
435 ptr = g_hash_table_lookup (contact_cache, lowercase_addr);
436 if (ptr != NULL && (check_contact == NULL || ptr == NOT_FOUND_BOOK)) {
437 g_free (lowercase_addr);
438 G_UNLOCK (contact_cache);
439
440 G_LOCK (search_addressbook_cancellables);
441 search_addressbook_cancellables = g_slist_remove (search_addressbook_cancellables, cancellable);
442 g_object_unref (cancellable);
443 G_UNLOCK (search_addressbook_cancellables);
444
445 return ptr != NOT_FOUND_BOOK;
446 }
447
448 book_query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, address);
449 query = e_book_query_to_string (book_query);
450 e_book_query_unref (book_query);
451
452 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
453 list = e_source_registry_list_sources (registry, extension_name);
454
455 for (link = list; link != NULL && !g_cancellable_is_cancelled (cancellable); link = g_list_next (link)) {
456 ESource *source = E_SOURCE (link->data);
457 ESourceExtension *extension;
458 const gchar *backend_name;
459 gboolean source_is_local;
460 gboolean autocomplete;
461
462 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
463 extension = e_source_get_extension (source, extension_name);
464
465 backend_name = e_source_backend_get_backend_name (
466 E_SOURCE_BACKEND (extension));
467
468 source_is_local = (g_strcmp0 (backend_name, "local") == 0);
469
470 if (local_only && !source_is_local)
471 continue;
472
473 extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE;
474 extension = e_source_get_extension (source, extension_name);
475
476 autocomplete = e_source_autocomplete_get_include_me (
477 E_SOURCE_AUTOCOMPLETE (extension));
478
479 if (!autocomplete)
480 continue;
481
482 addr_sources = g_list_prepend (
483 addr_sources, g_object_ref (source));
484 }
485
486 g_list_free_full (list, (GDestroyNotify) g_object_unref);
487
488 stop = g_cancellable_is_cancelled (cancellable);
489
490 for (link = addr_sources; !stop && !found && link != NULL
491 && !g_cancellable_is_cancelled (cancellable);
492 link = g_list_next (link)) {
493 ESource *source = E_SOURCE (link->data);
494 GSList *contacts;
495 EBookClient *book_client = NULL;
496 gboolean cached_book = FALSE;
497 const gchar *display_name;
498 const gchar *uid;
499 GError *err = NULL;
500
501 uid = e_source_get_uid (source);
502 display_name = e_source_get_display_name (source);
503
504 /* failed to load this book last time, skip it now */
505 if (g_hash_table_lookup (emu_broken_books_hash, uid) != NULL)
506 continue;
507
508 d (printf (" checking '%s'\n", e_source_get_uri (source)));
509
510 book_client = g_hash_table_lookup (emu_books_hash, uid);
511 if (!book_client) {
512 book_client = e_book_client_new (source, &err);
513
514 if (book_client == NULL) {
515 if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
516 g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
517 stop = TRUE;
518 } else if (err) {
519 gchar *source_uid;
520
521 source_uid = g_strdup (uid);
522
523 g_hash_table_insert (
524 emu_broken_books_hash,
525 source_uid, source_uid);
526
527 g_warning (
528 "%s: Unable to create addressbook '%s': %s",
529 G_STRFUNC,
530 display_name,
531 err->message);
532 }
533 g_clear_error (&err);
534 } else if (!stop && !try_open_book_client (book_client, TRUE, cancellable, &err)) {
535 g_object_unref (book_client);
536 book_client = NULL;
537
538 if (err && (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
539 g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
540 stop = TRUE;
541 } else if (err) {
542 gchar *source_uid;
543
544 source_uid = g_strdup (uid);
545
546 g_hash_table_insert (
547 emu_broken_books_hash,
548 source_uid, source_uid);
549
550 g_warning (
551 "%s: Unable to open addressbook '%s': %s",
552 G_STRFUNC,
553 display_name,
554 err->message);
555 }
556 g_clear_error (&err);
557 }
558 } else {
559 cached_book = TRUE;
560 }
561
562 if (book_client && !stop &&
563 e_book_client_get_contacts_sync (
564 book_client, query, &contacts, cancellable, &err)) {
565 if (contacts != NULL) {
566 if (!found_any) {
567 g_hash_table_insert (
568 contact_cache,
569 g_strdup (lowercase_addr),
570 book_client);
571 }
572 found_any = TRUE;
573
574 if (check_contact) {
575 GSList *l;
576
577 for (l = contacts; l && !found; l = l->next) {
578 EContact *contact = l->data;
579
580 found = check_contact (contact, user_data);
581 }
582 } else {
583 found = TRUE;
584 }
585
586 g_slist_foreach (contacts, (GFunc) g_object_unref, NULL);
587 g_slist_free (contacts);
588 }
589 } else if (book_client) {
590 stop = stop || (err &&
591 (g_error_matches (err, E_CLIENT_ERROR, E_CLIENT_ERROR_CANCELLED) ||
592 g_error_matches (err, G_IO_ERROR, G_IO_ERROR_CANCELLED)));
593 if (err && !stop) {
594 gchar *source_uid = g_strdup (uid);
595
596 g_hash_table_insert (
597 emu_broken_books_hash,
598 source_uid, source_uid);
599
600 g_warning (
601 "%s: Can't get contacts from '%s': %s",
602 G_STRFUNC,
603 display_name,
604 err->message);
605 }
606 g_clear_error (&err);
607 }
608
609 if (stop && !cached_book && book_client) {
610 g_object_unref (book_client);
611 } else if (!stop && book_client && !cached_book) {
612 g_hash_table_insert (
613 emu_books_hash, g_strdup (uid), book_client);
614 }
615 }
616
617 g_list_free_full (addr_sources, (GDestroyNotify) g_object_unref);
618
619 g_free (query);
620
621 if (!found_any) {
622 g_hash_table_insert (contact_cache, lowercase_addr, NOT_FOUND_BOOK);
623 lowercase_addr = NULL;
624 }
625
626 G_UNLOCK (contact_cache);
627
628 g_free (lowercase_addr);
629
630 G_LOCK (search_addressbook_cancellables);
631 search_addressbook_cancellables = g_slist_remove (search_addressbook_cancellables, cancellable);
632 g_object_unref (cancellable);
633 G_UNLOCK (search_addressbook_cancellables);
634
635 return found_any;
636 }
637
638 gboolean
639 em_utils_in_addressbook (ESourceRegistry *registry,
640 CamelInternetAddress *iaddr,
641 gboolean local_only,
642 GCancellable *cancellable)
643 {
644 const gchar *addr;
645
646 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
647
648 /* TODO: check all addresses? */
649 if (iaddr == NULL || !camel_internet_address_get (iaddr, 0, NULL, &addr))
650 return FALSE;
651
652 return search_address_in_addressbooks (
653 registry, addr, local_only, NULL, NULL, cancellable);
654 }
655
656 static gboolean
657 extract_photo_data (EContact *contact,
658 gpointer user_data)
659 {
660 EContactPhoto **photo = user_data;
661
662 g_return_val_if_fail (contact != NULL, FALSE);
663 g_return_val_if_fail (user_data != NULL, FALSE);
664
665 *photo = e_contact_get (contact, E_CONTACT_PHOTO);
666 if (!*photo)
667 *photo = e_contact_get (contact, E_CONTACT_LOGO);
668
669 return *photo != NULL;
670 }
671
672 typedef struct _PhotoInfo {
673 gchar *address;
674 EContactPhoto *photo;
675 } PhotoInfo;
676
677 static void
678 emu_free_photo_info (PhotoInfo *pi)
679 {
680 if (!pi)
681 return;
682
683 if (pi->address)
684 g_free (pi->address);
685 if (pi->photo)
686 e_contact_photo_free (pi->photo);
687 g_free (pi);
688 }
689
690 G_LOCK_DEFINE_STATIC (photos_cache);
691 static GSList *photos_cache = NULL; /* list of PhotoInfo-s */
692
693 CamelMimePart *
694 em_utils_contact_photo (ESourceRegistry *registry,
695 CamelInternetAddress *cia,
696 gboolean local_only,
697 GCancellable *cancellable)
698 {
699 const gchar *addr = NULL;
700 CamelMimePart *part = NULL;
701 EContactPhoto *photo = NULL;
702 GSList *p, *last = NULL;
703 gint cache_len;
704
705 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
706
707 if (cia == NULL || !camel_internet_address_get (cia, 0, NULL, &addr) || !addr) {
708 return NULL;
709 }
710
711 G_LOCK (photos_cache);
712
713 /* search a cache first */
714 cache_len = 0;
715 last = NULL;
716 for (p = photos_cache; p; p = p->next) {
717 PhotoInfo *pi = p->data;
718
719 if (!pi)
720 continue;
721
722 if (g_ascii_strcasecmp (addr, pi->address) == 0) {
723 photo = pi->photo;
724 break;
725 }
726
727 cache_len++;
728 last = p;
729 }
730
731 /* !p means the address had not been found in the cache */
732 if (!p && search_address_in_addressbooks (
733 registry, addr, local_only, extract_photo_data, &photo, cancellable)) {
734
735 PhotoInfo *pi;
736
737 /* keep only up to 10 photos in memory */
738 if (last && (cache_len >= 10)) {
739 pi = last->data;
740 photos_cache = g_slist_remove (photos_cache, pi);
741
742 if (pi)
743 emu_free_photo_info (pi);
744 }
745
746 pi = g_new0 (PhotoInfo, 1);
747 pi->address = g_strdup (addr);
748 pi->photo = photo;
749
750 photos_cache = g_slist_prepend (photos_cache, pi);
751 }
752
753 /* some photo found, use it */
754 if (photo) {
755 /* Form a mime part out of the photo */
756 part = camel_mime_part_new ();
757
758 if (photo->type == E_CONTACT_PHOTO_TYPE_INLINED) {
759 camel_mime_part_set_content (
760 part,
761 (const gchar *) photo->data.inlined.data,
762 photo->data.inlined.length, "image/jpeg");
763 } else {
764 gchar *s = g_filename_from_uri (photo->data.uri, NULL, NULL);
765 camel_mime_part_set_filename (part, s);
766 g_free (s);
767 }
768 }
769
770 G_UNLOCK (photos_cache);
771
772 return part;
773 }
774
775 /* list of email addresses (strings) to remove from local cache of photos and
776 * contacts, but only if the photo doesn't exist or is an not-found contact */
777 void
778 emu_remove_from_mail_cache (const GSList *addresses)
779 {
780 const GSList *a;
781 GSList *p;
782 CamelInternetAddress *cia;
783
784 cia = camel_internet_address_new ();
785
786 for (a = addresses; a; a = a->next) {
787 const gchar *addr = NULL;
788
789 if (!a->data)
790 continue;
791
792 if (camel_address_decode ((CamelAddress *) cia, a->data) != -1 &&
793 camel_internet_address_get (cia, 0, NULL, &addr) && addr) {
794 gchar *lowercase_addr = g_utf8_strdown (addr, -1);
795
796 G_LOCK (contact_cache);
797 if (g_hash_table_lookup (contact_cache, lowercase_addr) == NOT_FOUND_BOOK)
798 g_hash_table_remove (contact_cache, lowercase_addr);
799 G_UNLOCK (contact_cache);
800
801 g_free (lowercase_addr);
802
803 G_LOCK (photos_cache);
804 for (p = photos_cache; p; p = p->next) {
805 PhotoInfo *pi = p->data;
806
807 if (pi && !pi->photo && g_ascii_strcasecmp (pi->address, addr) == 0) {
808 photos_cache = g_slist_remove (photos_cache, pi);
809 emu_free_photo_info (pi);
810 break;
811 }
812 }
813 G_UNLOCK (photos_cache);
814 }
815 }
816
817 g_object_unref (cia);
818 }
819
820 void
821 emu_remove_from_mail_cache_1 (const gchar *address)
822 {
823 GSList *l;
824
825 g_return_if_fail (address != NULL);
826
827 l = g_slist_append (NULL, (gpointer) address);
828
829 emu_remove_from_mail_cache (l);
830
831 g_slist_free (l);
832 }
833
834 struct FreeMailCacheData
835 {
836 GDestroyNotify done_cb;
837 gpointer user_data;
838 };
839
840 static gboolean
841 free_mail_cache_idle (gpointer user_data)
842 {
843 struct FreeMailCacheData *fmc = user_data;
844
845 g_return_val_if_fail (fmc != NULL, FALSE);
846
847 if (fmc->done_cb)
848 fmc->done_cb (fmc->user_data);
849 g_free (fmc);
850
851 return FALSE;
852 }
853
854 static gpointer
855 free_mail_cache_thread (gpointer user_data)
856 {
857 g_return_val_if_fail (user_data != NULL, NULL);
858
859 G_LOCK (contact_cache);
860
861 if (emu_books_hash) {
862 g_hash_table_destroy (emu_books_hash);
863 emu_books_hash = NULL;
864 }
865
866 if (emu_broken_books_hash) {
867 g_hash_table_destroy (emu_broken_books_hash);
868 emu_broken_books_hash = NULL;
869 }
870
871 if (contact_cache) {
872 g_hash_table_destroy (contact_cache);
873 contact_cache = NULL;
874 }
875
876 G_UNLOCK (contact_cache);
877
878 G_LOCK (photos_cache);
879
880 g_slist_foreach (photos_cache, (GFunc) emu_free_photo_info, NULL);
881 g_slist_free (photos_cache);
882 photos_cache = NULL;
883
884 G_UNLOCK (photos_cache);
885
886 /* Prioritize ahead of GTK+ redraws. */
887 g_idle_add_full (
888 G_PRIORITY_HIGH_IDLE,
889 free_mail_cache_idle, user_data, NULL);
890
891 return NULL;
892 }
893
894 /* frees all data created by call of em_utils_in_addressbook() or
895 * em_utils_contact_photo(); done_cb is called when freeing is done,
896 * in an idle callback
897 */
898 void
899 emu_free_mail_cache (GDestroyNotify done_cb,
900 gpointer user_data)
901 {
902 struct FreeMailCacheData *fmc;
903 GThread *thread;
904
905 G_LOCK (search_addressbook_cancellables);
906 g_slist_foreach (search_addressbook_cancellables, (GFunc) g_cancellable_cancel, NULL);
907 G_UNLOCK (search_addressbook_cancellables);
908
909 fmc = g_new0 (struct FreeMailCacheData, 1);
910 fmc->done_cb = done_cb;
911 fmc->user_data = user_data;
912
913 thread = g_thread_new (NULL, free_mail_cache_thread, fmc);
914 g_thread_unref (thread);
915 }
916
917 static ESource *
918 guess_mail_account_from_folder (ESourceRegistry *registry,
919 CamelFolder *folder,
920 const gchar *message_uid)
921 {
922 ESource *source;
923 CamelStore *store;
924 const gchar *uid;
925
926 /* Lookup an ESource by CamelStore UID. */
927 store = camel_folder_get_parent_store (folder);
928 if (message_uid && folder && CAMEL_IS_VEE_STORE (store)) {
929 CamelMessageInfo *mi = camel_folder_get_message_info (folder, message_uid);
930 if (mi) {
931 CamelFolder *location;
932
933 location = camel_vee_folder_get_location (CAMEL_VEE_FOLDER (folder), (CamelVeeMessageInfo *) mi, NULL);
934 if (location)
935 store = camel_folder_get_parent_store (location);
936 camel_folder_free_message_info (folder, mi);
937 }
938 }
939
940 uid = camel_service_get_uid (CAMEL_SERVICE (store));
941 source = e_source_registry_ref_source (registry, uid);
942
943 /* If we found an ESource, make sure it's a mail account. */
944 if (source != NULL) {
945 const gchar *extension_name;
946
947 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
948 if (!e_source_has_extension (source, extension_name)) {
949 g_object_unref (source);
950 source = NULL;
951 }
952 }
953
954 return source;
955 }
956
957 static ESource *
958 guess_mail_account_from_message (ESourceRegistry *registry,
959 CamelMimeMessage *message)
960 {
961 ESource *source = NULL;
962 const gchar *uid;
963
964 /* Lookup an ESource by 'X-Evolution-Source' header. */
965 uid = camel_mime_message_get_source (message);
966 if (uid != NULL)
967 source = e_source_registry_ref_source (registry, uid);
968
969 /* If we found an ESource, make sure it's a mail account. */
970 if (source != NULL) {
971 const gchar *extension_name;
972
973 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
974 if (!e_source_has_extension (source, extension_name)) {
975 g_object_unref (source);
976 source = NULL;
977 }
978 }
979
980 return source;
981 }
982
983 ESource *
984 em_utils_guess_mail_account (ESourceRegistry *registry,
985 CamelMimeMessage *message,
986 CamelFolder *folder,
987 const gchar *message_uid)
988 {
989 ESource *source = NULL;
990 const gchar *newsgroups;
991
992 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
993 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
994
995 if (folder != NULL)
996 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
997
998 /* check for newsgroup header */
999 newsgroups = camel_medium_get_header (
1000 CAMEL_MEDIUM (message), "Newsgroups");
1001 if (folder != NULL && newsgroups != NULL)
1002 source = guess_mail_account_from_folder (registry, folder, message_uid);
1003
1004 /* check for source folder */
1005 if (source == NULL && folder != NULL)
1006 source = guess_mail_account_from_folder (registry, folder, message_uid);
1007
1008 /* then message source */
1009 if (source == NULL)
1010 source = guess_mail_account_from_message (registry, message);
1011
1012 return source;
1013 }
1014
1015 ESource *
1016 em_utils_guess_mail_identity (ESourceRegistry *registry,
1017 CamelMimeMessage *message,
1018 CamelFolder *folder,
1019 const gchar *message_uid)
1020 {
1021 ESource *source;
1022 ESourceExtension *extension;
1023 const gchar *extension_name;
1024 const gchar *uid;
1025
1026 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1027 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1028
1029 if (folder != NULL)
1030 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
1031
1032 source = em_utils_guess_mail_account (registry, message, folder, message_uid);
1033
1034 if (source == NULL)
1035 return NULL;
1036
1037 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1038 extension = e_source_get_extension (source, extension_name);
1039
1040 uid = e_source_mail_account_get_identity_uid (
1041 E_SOURCE_MAIL_ACCOUNT (extension));
1042 if (uid == NULL)
1043 return NULL;
1044
1045 source = e_source_registry_ref_source (registry, uid);
1046 if (source == NULL)
1047 return NULL;
1048
1049 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1050 if (!e_source_has_extension (source, extension_name)) {
1051 g_object_unref (source);
1052 return NULL;
1053 }
1054
1055 return source;
1056 }
1057
1058 static gboolean
1059 mail_account_in_recipients (ESourceRegistry *registry,
1060 ESource *source,
1061 GHashTable *recipients)
1062 {
1063 ESourceExtension *extension;
1064 const gchar *extension_name;
1065 const gchar *uid;
1066 gboolean match = FALSE;
1067 gchar *address;
1068
1069 /* Disregard disabled mail accounts. */
1070 if (!e_source_get_enabled (source))
1071 return FALSE;
1072
1073 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1074 extension = e_source_get_extension (source, extension_name);
1075
1076 uid = e_source_mail_account_get_identity_uid (
1077 E_SOURCE_MAIL_ACCOUNT (extension));
1078 if (uid == NULL)
1079 return FALSE;
1080
1081 source = e_source_registry_ref_source (registry, uid);
1082 if (source == NULL)
1083 return FALSE;
1084
1085 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1086 if (!e_source_has_extension (source, extension_name)) {
1087 g_object_unref (source);
1088 return FALSE;
1089 }
1090
1091 extension = e_source_get_extension (source, extension_name);
1092
1093 address = e_source_mail_identity_dup_address (
1094 E_SOURCE_MAIL_IDENTITY (extension));
1095
1096 g_object_unref (source);
1097
1098 if (address != NULL) {
1099 match = (g_hash_table_lookup (recipients, address) != NULL);
1100 g_free (address);
1101 }
1102
1103 return match;
1104 }
1105
1106 ESource *
1107 em_utils_guess_mail_account_with_recipients_and_sort (ESourceRegistry *registry,
1108 CamelMimeMessage *message,
1109 CamelFolder *folder,
1110 const gchar *message_uid,
1111 EMailUtilsSourtSourcesFunc sort_func,
1112 gpointer sort_func_data)
1113 {
1114 ESource *source = NULL;
1115 GHashTable *recipients;
1116 CamelInternetAddress *addr;
1117 GList *list, *iter;
1118 const gchar *extension_name;
1119 const gchar *type;
1120 const gchar *key;
1121
1122 /* This policy is subject to debate and tweaking,
1123 * but please also document the rational here. */
1124
1125 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1126 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1127
1128 /* Build a set of email addresses in which to test for membership.
1129 * Only the keys matter here; the values just need to be non-NULL. */
1130 recipients = g_hash_table_new (g_str_hash, g_str_equal);
1131
1132 type = CAMEL_RECIPIENT_TYPE_TO;
1133 addr = camel_mime_message_get_recipients (message, type);
1134 if (addr != NULL) {
1135 gint index = 0;
1136
1137 while (camel_internet_address_get (addr, index++, NULL, &key))
1138 g_hash_table_insert (
1139 recipients, (gpointer) key,
1140 GINT_TO_POINTER (1));
1141 }
1142
1143 type = CAMEL_RECIPIENT_TYPE_CC;
1144 addr = camel_mime_message_get_recipients (message, type);
1145 if (addr != NULL) {
1146 gint index = 0;
1147
1148 while (camel_internet_address_get (addr, index++, NULL, &key))
1149 g_hash_table_insert (
1150 recipients, (gpointer) key,
1151 GINT_TO_POINTER (1));
1152 }
1153
1154 /* First Preference: We were given a folder that maps to an
1155 * enabled mail account, and that account's address appears
1156 * in the list of To: or Cc: recipients. */
1157
1158 if (folder != NULL)
1159 source = guess_mail_account_from_folder (
1160 registry, folder, message_uid);
1161
1162 if (source == NULL)
1163 goto second_preference;
1164
1165 if (mail_account_in_recipients (registry, source, recipients))
1166 goto exit;
1167
1168 second_preference:
1169
1170 /* Second Preference: Choose any enabled mail account whose
1171 * address appears in the list to To: or Cc: recipients. */
1172
1173 if (source != NULL) {
1174 g_object_unref (source);
1175 source = NULL;
1176 }
1177
1178 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1179 list = e_source_registry_list_sources (registry, extension_name);
1180
1181 if (sort_func)
1182 sort_func (&list, sort_func_data);
1183
1184 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1185 ESource *temp = E_SOURCE (iter->data);
1186
1187 if (em_utils_is_source_enabled_with_parents (registry, temp) &&
1188 mail_account_in_recipients (registry, temp, recipients)) {
1189 source = g_object_ref (temp);
1190 break;
1191 }
1192 }
1193
1194 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1195
1196 if (source != NULL)
1197 goto exit;
1198
1199 /* Last Preference: Defer to em_utils_guess_mail_account(). */
1200 source = em_utils_guess_mail_account (
1201 registry, message, folder, message_uid);
1202
1203 exit:
1204 g_hash_table_destroy (recipients);
1205
1206 return source;
1207 }
1208
1209 ESource *
1210 em_utils_guess_mail_identity_with_recipients_and_sort (ESourceRegistry *registry,
1211 CamelMimeMessage *message,
1212 CamelFolder *folder,
1213 const gchar *message_uid,
1214 EMailUtilsSourtSourcesFunc sort_func,
1215 gpointer sort_func_data)
1216 {
1217 ESource *source;
1218 ESourceExtension *extension;
1219 const gchar *extension_name;
1220 const gchar *uid;
1221
1222 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1223 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), NULL);
1224
1225 source = em_utils_guess_mail_account_with_recipients_and_sort (
1226 registry, message, folder, message_uid, sort_func, sort_func_data);
1227
1228 if (source == NULL)
1229 return NULL;
1230
1231 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1232 extension = e_source_get_extension (source, extension_name);
1233
1234 uid = e_source_mail_account_get_identity_uid (
1235 E_SOURCE_MAIL_ACCOUNT (extension));
1236 if (uid == NULL)
1237 return NULL;
1238
1239 source = e_source_registry_ref_source (registry, uid);
1240 if (source == NULL)
1241 return NULL;
1242
1243 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1244 if (!e_source_has_extension (source, extension_name)) {
1245 g_object_unref (source);
1246 return NULL;
1247 }
1248
1249 return source;
1250 }
1251
1252 ESource *
1253 em_utils_guess_mail_account_with_recipients (ESourceRegistry *registry,
1254 CamelMimeMessage *message,
1255 CamelFolder *folder,
1256 const gchar *message_uid)
1257 {
1258 return em_utils_guess_mail_account_with_recipients_and_sort (registry, message, folder, message_uid, NULL, NULL);
1259 }
1260
1261 ESource *
1262 em_utils_guess_mail_identity_with_recipients (ESourceRegistry *registry,
1263 CamelMimeMessage *message,
1264 CamelFolder *folder,
1265 const gchar *message_uid)
1266 {
1267 return em_utils_guess_mail_identity_with_recipients_and_sort (registry, message, folder, message_uid, NULL, NULL);
1268 }
1269
1270 ESource *
1271 em_utils_ref_mail_identity_for_store (ESourceRegistry *registry,
1272 CamelStore *store)
1273 {
1274 ESourceMailAccount *extension;
1275 ESource *source;
1276 const gchar *extension_name;
1277 const gchar *store_uid;
1278 gchar *identity_uid;
1279
1280 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
1281 g_return_val_if_fail (CAMEL_IS_STORE (store), NULL);
1282
1283 store_uid = camel_service_get_uid (CAMEL_SERVICE (store));
1284 g_return_val_if_fail (store_uid != NULL, NULL);
1285
1286 source = e_source_registry_ref_source (registry, store_uid);
1287 g_return_val_if_fail (source != NULL, NULL);
1288
1289 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1290 extension = e_source_get_extension (source, extension_name);
1291 identity_uid = e_source_mail_account_dup_identity_uid (extension);
1292
1293 g_object_unref (source);
1294 source = NULL;
1295
1296 if (identity_uid != NULL) {
1297 source = e_source_registry_ref_source (registry, identity_uid);
1298 g_free (identity_uid);
1299 }
1300
1301 return source;
1302 }
1303
1304 gboolean
1305 em_utils_is_source_enabled_with_parents (ESourceRegistry *registry,
1306 ESource *source)
1307 {
1308 ESource *parent;
1309 const gchar *parent_uid;
1310
1311 g_return_val_if_fail (registry != NULL, FALSE);
1312 g_return_val_if_fail (source != NULL, FALSE);
1313
1314 if (!e_source_get_enabled (source))
1315 return FALSE;
1316
1317 parent = g_object_ref (source);
1318 while (parent_uid = e_source_get_parent (parent), parent_uid) {
1319 ESource *next = e_source_registry_ref_source (registry, parent_uid);
1320
1321 if (!next)
1322 break;
1323
1324 g_object_unref (parent);
1325
1326 if (!e_source_get_enabled (next)) {
1327 g_object_unref (next);
1328 return FALSE;
1329 }
1330
1331 parent = next;
1332 }
1333
1334 g_object_unref (parent);
1335
1336 return TRUE;
1337 }
1338
1339 /**
1340 * em_utils_uids_free:
1341 * @uids: array of uids
1342 *
1343 * Frees the array of uids pointed to by @uids back to the system.
1344 **/
1345 void
1346 em_utils_uids_free (GPtrArray *uids)
1347 {
1348 gint i;
1349
1350 for (i = 0; i < uids->len; i++)
1351 g_free (uids->pdata[i]);
1352
1353 g_ptr_array_free (uids, TRUE);
1354 }
1355
1356 /* Returns TRUE if CamelURL points to a local mbox file. */
1357 gboolean
1358 em_utils_is_local_delivery_mbox_file (CamelURL *url)
1359 {
1360 g_return_val_if_fail (url != NULL, FALSE);
1361
1362 return g_str_equal (url->protocol, "mbox") &&
1363 (url->path != NULL) &&
1364 g_file_test (url->path, G_FILE_TEST_EXISTS) &&
1365 !g_file_test (url->path, G_FILE_TEST_IS_DIR);
1366 }