aboutsummaryrefslogtreecommitdiffstats
path: root/mail
diff options
context:
space:
mode:
Diffstat (limited to 'mail')
-rw-r--r--mail/e-mail-reader.c4
-rw-r--r--mail/em-utils.c493
-rw-r--r--mail/em-utils.h4
3 files changed, 315 insertions, 186 deletions
diff --git a/mail/e-mail-reader.c b/mail/e-mail-reader.c
index e7d83f6d9c..cdb3761d60 100644
--- a/mail/e-mail-reader.c
+++ b/mail/e-mail-reader.c
@@ -96,7 +96,7 @@ action_mail_add_sender_cb (GtkAction *action,
* event. Kind of kludgey, but works for now. */
shell = e_shell_backend_get_shell (shell_backend);
e_shell_event (shell, "contact-quick-add-email", (gpointer) address);
-
+ emu_remove_from_mail_cache_1 (address);
exit:
em_utils_uids_free (uids);
}
@@ -132,7 +132,7 @@ action_add_to_address_book_cb (GtkAction *action,
* event. Kind of kludgey, but works for now. */
shell = e_shell_backend_get_shell (shell_backend);
e_shell_event (shell, "contact-quick-add-email", curl->path);
-
+ emu_remove_from_mail_cache_1 (curl->path);
exit:
camel_url_free (curl);
}
diff --git a/mail/em-utils.c b/mail/em-utils.c
index 6198fbde40..3c6c4ffee7 100644
--- a/mail/em-utils.c
+++ b/mail/em-utils.c
@@ -46,6 +46,8 @@
#include <camel/camel-url-scanner.h>
#include <camel/camel-file-utils.h>
+#include <libebook/e-book.h>
+
#include "em-filter-editor.h"
#include <glib/gi18n.h>
@@ -1805,43 +1807,39 @@ gchar *em_uri_to_camel(const gchar *euri)
}
/* ********************************************************************** */
-#include <libebook/e-book.h>
-
-struct _addr_node {
- gchar *addr;
- time_t stamp;
- gint found;
-};
-
-#define EMU_ADDR_CACHE_TIME (60*30) /* in seconds */
-
-static pthread_mutex_t emu_addr_lock = PTHREAD_MUTEX_INITIALIZER;
-static ESourceList *emu_addr_list;
-static GHashTable *emu_addr_cache;
/* runs sync, in main thread */
static gpointer
-emu_addr_setup(gpointer dummy)
+emu_addr_setup (gpointer user_data)
{
GError *err = NULL;
+ ESourceList **psource_list = user_data;
- emu_addr_cache = g_hash_table_new(g_str_hash, g_str_equal);
-
- if (!e_book_get_addressbooks(&emu_addr_list, &err))
- g_error_free(err);
+ if (!e_book_get_addressbooks (psource_list, &err))
+ g_error_free (err);
return NULL;
}
static void
-emu_addr_cancel_book(gpointer data)
+emu_addr_cancel_book (gpointer data)
{
EBook *book = data;
GError *err = NULL;
/* we dunna care if this fails, its just the best we can try */
- e_book_cancel(book, &err);
- g_clear_error(&err);
+ e_book_cancel (book, &err);
+ g_clear_error (&err);
+}
+
+static void
+emu_addr_cancel_stop (gpointer data)
+{
+ gboolean *stop = data;
+
+ g_return_if_fail (stop != NULL);
+
+ *stop = TRUE;
}
struct TryOpenEBookStruct {
@@ -1916,247 +1914,374 @@ try_open_e_book (EBook *book, gboolean only_if_exists, GError **error)
return data.result && (!error || !*error);
}
-static gboolean
-is_local (ESourceGroup *group)
-{
- return group &&
- e_source_group_peek_base_uri (group) &&
- g_str_has_prefix (e_source_group_peek_base_uri (group), "file://");
-}
+#define NOT_FOUND_BOOK (GINT_TO_POINTER (1))
-gboolean
-em_utils_in_addressbook (CamelInternetAddress *iaddr, gboolean local_only)
+G_LOCK_DEFINE_STATIC (contact_cache);
+static GHashTable *contact_cache = NULL; /* key is lowercased contact email; value is EBook pointer (just for comparison) where it comes from */
+static GHashTable *emu_books_hash = NULL; /* key is source ID; value is pointer to EBook */
+static ESourceList *emu_books_source_list = NULL;
+
+static gboolean
+search_address_in_addressbooks (const gchar *address, gboolean local_only, gboolean (*check_contact) (EContact *contact, gpointer user_data), gpointer user_data)
{
- GError *err = NULL;
- GSList *s, *g, *addr_sources = NULL;
- gint stop = FALSE, found = FALSE;
+ gboolean found = FALSE, stop = FALSE, found_any = FALSE;
+ gchar *lowercase_addr;
+ gpointer ptr;
EBookQuery *query;
- const gchar *addr;
- struct _addr_node *node;
- time_t now;
+ GSList *s, *g, *addr_sources = NULL;
- /* TODO: check all addresses? */
- if (iaddr == NULL
- || !camel_internet_address_get(iaddr, 0, NULL, &addr))
+ if (!address || !*address)
return FALSE;
- pthread_mutex_lock(&emu_addr_lock);
+ G_LOCK (contact_cache);
- if (emu_addr_cache == NULL) {
- mail_call_main(MAIL_CALL_p_p, (MailMainFunc)emu_addr_setup, NULL);
+ if (!emu_books_source_list) {
+ mail_call_main (MAIL_CALL_p_p, (MailMainFunc)emu_addr_setup, &emu_books_source_list);
+ emu_books_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ contact_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
}
- if (emu_addr_list == NULL) {
- pthread_mutex_unlock(&emu_addr_lock);
+ if (!emu_books_source_list) {
+ G_UNLOCK (contact_cache);
return FALSE;
}
- now = time(NULL);
-
- d(printf("Checking '%s' is in addressbook", addr));
-
- node = g_hash_table_lookup(emu_addr_cache, addr);
- if (node) {
- d(printf(" -> cached, found %s\n", node->found?"yes":"no"));
- if (node->stamp + EMU_ADDR_CACHE_TIME > now) {
- found = node->found;
- pthread_mutex_unlock(&emu_addr_lock);
- return found;
- }
- d(printf(" but expired!\n"));
- } else {
- d(printf(" -> not found in cache\n"));
- node = g_malloc0(sizeof(*node));
- node->addr = g_strdup(addr);
- g_hash_table_insert(emu_addr_cache, node->addr, node);
+ lowercase_addr = g_utf8_strdown (address, -1);
+ ptr = g_hash_table_lookup (contact_cache, lowercase_addr);
+ if (ptr != NULL && (check_contact == NULL || ptr == NOT_FOUND_BOOK)) {
+ g_free (lowercase_addr);
+ G_UNLOCK (contact_cache);
+ return ptr != NOT_FOUND_BOOK;
}
- query = e_book_query_field_test(E_CONTACT_EMAIL, E_BOOK_QUERY_IS, addr);
+ query = e_book_query_field_test (E_CONTACT_EMAIL, E_BOOK_QUERY_IS, address);
+
+ for (g = e_source_list_peek_groups (emu_books_source_list); g; g = g_slist_next (g)) {
+ ESourceGroup *group = g->data;
- /* FIXME: this aint threadsafe by any measure, but what can you do eh??? */
+ if (!group)
+ continue;
- for (g = e_source_list_peek_groups(emu_addr_list);g;g=g_slist_next(g)) {
- if (local_only && !is_local (g->data))
+ if (local_only && !(e_source_group_peek_base_uri (group) && g_str_has_prefix (e_source_group_peek_base_uri (group), "file://")))
continue;
- for (s = e_source_group_peek_sources((ESourceGroup *)g->data);s;s=g_slist_next(s)) {
- ESource *src = s->data;
- const gchar *completion = e_source_get_property (src, "completion");
+ for (s = e_source_group_peek_sources (group); s; s = g_slist_next (s)) {
+ ESource *source = s->data;
+ const gchar *completion = e_source_get_property (source, "completion");
- if (completion && !g_ascii_strcasecmp (completion, "true")) {
- addr_sources = g_slist_prepend(addr_sources, src);
- g_object_ref(src);
+ if (completion && g_ascii_strcasecmp (completion, "true") == 0) {
+ addr_sources = g_slist_prepend (addr_sources, g_object_ref (source));
}
}
}
- for (s = addr_sources;!stop && !found && s;s=g_slist_next(s)) {
+ for (s = addr_sources; !stop && !found && s; s = g_slist_next (s)) {
ESource *source = s->data;
GList *contacts;
- EBook *book;
- GHook *hook;
+ EBook *book = NULL;
+ GHook *hook_book, *hook_stop;
+ gboolean cached_book = FALSE;
+ GError *err = NULL;
d(printf(" checking '%s'\n", e_source_get_uri(source)));
- /* could this take a while? no way to cancel it? */
- book = e_book_new(source, &err);
+ hook_book = mail_cancel_hook_add (emu_addr_cancel_book, book);
+ hook_stop = mail_cancel_hook_add (emu_addr_cancel_stop, &stop);
- if (book == NULL) {
- if (err && !g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED))
- g_warning ("%s: Unable to create addressbook: %s", G_STRFUNC, err->message);
- g_clear_error(&err);
- continue;
+ book = g_hash_table_lookup (emu_books_hash, e_source_peek_uid (source));
+ if (!book) {
+ book = e_book_new (source, &err);
+
+ if (book == NULL) {
+ if (err && g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED)) {
+ stop = TRUE;
+ } else if (err) {
+ g_warning ("%s: Unable to create addressbook: %s", G_STRFUNC, err->message);
+ }
+ g_clear_error (&err);
+ } else if (!stop && !try_open_e_book (book, TRUE, &err)) {
+ g_object_unref (book);
+ book = NULL;
+
+ if (err && g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED)) {
+ stop = TRUE;
+ } else if (err) {
+ g_warning ("%s: Unable to open addressbook: %s", G_STRFUNC, err->message);
+ }
+ g_clear_error (&err);
+ }
+ } else {
+ cached_book = TRUE;
}
- g_clear_error(&err);
+ if (book && !stop && e_book_get_contacts (book, query, &contacts, &err)) {
+ if (contacts != NULL) {
+ if (!found_any) {
+ g_hash_table_insert (contact_cache, g_strdup (lowercase_addr), book);
+ }
+ found_any = TRUE;
+
+ if (check_contact) {
+ GList *l;
- hook = mail_cancel_hook_add(emu_addr_cancel_book, book);
+ for (l = contacts; l && !found; l = l->next) {
+ EContact *contact = l->data;
- /* ignore errors, but cancellation errors we don't try to go further either */
- if (!try_open_e_book (book, TRUE, &err)
- || !e_book_get_contacts(book, query, &contacts, &err)) {
- stop = err && g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED);
- mail_cancel_hook_remove(hook);
- g_object_unref(book);
+ found = check_contact (contact, user_data);
+ }
+ } else {
+ found = TRUE;
+ }
+
+ g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
+ g_list_free (contacts);
+ }
+ } else if (book) {
+ stop = stop || (err && g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED));
if (err && !stop)
g_warning ("%s: Can't get contacts: %s", G_STRFUNC, err->message);
- g_clear_error(&err);
- continue;
+ g_clear_error (&err);
}
- mail_cancel_hook_remove(hook);
-
- if (contacts != NULL) {
- found = TRUE;
- g_list_foreach(contacts, (GFunc)g_object_unref, NULL);
- g_list_free(contacts);
- }
+ mail_cancel_hook_remove (hook_book);
+ mail_cancel_hook_remove (hook_stop);
stop = stop || camel_operation_cancel_check (NULL);
- d(printf(" %s\n", stop?"found":"not found"));
-
- g_object_unref(book);
+ if (stop && !cached_book && book) {
+ g_object_unref (book);
+ } else if (!stop && book && !cached_book) {
+ g_hash_table_insert (emu_books_hash, g_strdup (e_source_peek_uid (source)), book);
+ }
}
- g_slist_free(addr_sources);
+ g_slist_foreach (addr_sources, (GFunc) g_object_unref, NULL);
+ g_slist_free (addr_sources);
- if (!stop) {
- node->found = found;
- node->stamp = now;
+ e_book_query_unref (query);
+
+ if (!found_any) {
+ g_hash_table_insert (contact_cache, lowercase_addr, NOT_FOUND_BOOK);
+ lowercase_addr = NULL;
}
- e_book_query_unref(query);
+ G_UNLOCK (contact_cache);
- pthread_mutex_unlock(&emu_addr_lock);
+ g_free (lowercase_addr);
- return found;
+ return found_any;
}
-CamelMimePart *
-em_utils_contact_photo (CamelInternetAddress *cia, gboolean local)
+gboolean
+em_utils_in_addressbook (CamelInternetAddress *iaddr, gboolean local_only)
{
const gchar *addr;
- gint stop = FALSE, found = FALSE;
- GSList *s, *g, *addr_sources = NULL;
- GError *err = NULL;
- EBookQuery *query = NULL;
- ESource *source = NULL;
- GList *contacts = NULL;
- EContact *contact = NULL;
+
+ /* TODO: check all addresses? */
+ if (iaddr == NULL || !camel_internet_address_get (iaddr, 0, NULL, &addr))
+ return FALSE;
+
+ return search_address_in_addressbooks (addr, local_only, NULL, NULL);
+}
+
+static gboolean
+extract_photo_data (EContact *contact, gpointer user_data)
+{
+ EContactPhoto **photo = user_data;
+
+ g_return_val_if_fail (contact != NULL, FALSE);
+ g_return_val_if_fail (user_data != NULL, FALSE);
+
+ *photo = e_contact_get (contact, E_CONTACT_PHOTO);
+ if (!*photo)
+ *photo = e_contact_get (contact, E_CONTACT_LOGO);
+
+ return *photo != NULL;
+}
+
+typedef struct _PhotoInfo {
+ gchar *address;
+ EContactPhoto *photo;
+} PhotoInfo;
+
+static void
+emu_free_photo_info (PhotoInfo *pi)
+{
+ if (!pi)
+ return;
+
+ if (pi->address)
+ g_free (pi->address);
+ if (pi->photo)
+ e_contact_photo_free (pi->photo);
+ g_free (pi);
+}
+
+G_LOCK_DEFINE_STATIC (photos_cache);
+static GSList *photos_cache = NULL; /* list of PhotoInfo-s */
+
+CamelMimePart *
+em_utils_contact_photo (CamelInternetAddress *cia, gboolean local_only)
+{
+ const gchar *addr = NULL;
+ CamelMimePart *part = NULL;
EContactPhoto *photo = NULL;
- EBook *book = NULL;
- CamelMimePart *part;
+ GSList *p, *first_not_null = NULL;
+ gint count_not_null = 0;
- if (cia == NULL || !camel_internet_address_get(cia, 0, NULL, &addr)) {
+ if (cia == NULL || !camel_internet_address_get (cia, 0, NULL, &addr) || !addr) {
return NULL;
}
- if (!emu_addr_list) {
- if (!e_book_get_addressbooks(&emu_addr_list, &err)) {
- g_error_free(err);
- return NULL;
- }
- }
+ G_LOCK (photos_cache);
+
+ /* search a cache first */
+ for (p = photos_cache; p; p = p->next) {
+ PhotoInfo *pi = p->data;
- query = e_book_query_field_test(E_CONTACT_EMAIL, E_BOOK_QUERY_IS, addr);
- for (g = e_source_list_peek_groups(emu_addr_list); g; g = g_slist_next(g)) {
- if (local && !is_local (g->data))
+ if (!pi)
continue;
- for (s = e_source_group_peek_sources((ESourceGroup *)g->data); s; s=g_slist_next(s)) {
- ESource *src = s->data;
- const gchar *completion = e_source_get_property (src, "completion");
+ if (pi->photo) {
+ if (!first_not_null)
+ first_not_null = p;
+ count_not_null++;
+ }
- if (completion && !g_ascii_strcasecmp (completion, "true")) {
- addr_sources = g_slist_prepend(addr_sources, src);
- g_object_ref(src);
- }
+ if (g_ascii_strcasecmp (addr, pi->address) == 0) {
+ photo = pi->photo;
+ break;
}
}
- for (s = addr_sources;!stop && !found && s;s=g_slist_next(s)) {
- source = s->data;
+ /* !p means the address had not been found in the cache */
+ if (!p && search_address_in_addressbooks (addr, local_only, extract_photo_data, &photo)) {
+ PhotoInfo *pi;
- book = e_book_new(source, &err);
- if (!book) {
- if (err && !g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED))
- g_warning ("%s: Unable to create addressbook: %s", G_STRFUNC, err->message);
- g_clear_error (&err);
- continue;
+ if (photo && photo->type != E_CONTACT_PHOTO_TYPE_INLINED) {
+ e_contact_photo_free (photo);
+ photo = NULL;
}
- g_clear_error (&err);
+ /* keep only up to 10 photos in memory */
+ if (photo && count_not_null >= 10 && first_not_null) {
+ pi = first_not_null->data;
- if (!try_open_e_book (book, TRUE, &err)
- || !e_book_get_contacts(book, query, &contacts, &err)) {
- stop = err && g_error_matches (err, E_BOOK_ERROR, E_BOOK_ERROR_CANCELLED);
- g_object_unref(book);
- if (err && !stop)
- g_warning ("%s: Can't get contacts: %s", G_STRFUNC, err->message);
- g_clear_error(&err);
- continue;
- }
- g_clear_error (&err);
-
- if (contacts != NULL) {
- found = TRUE;
-
- /* Doesn't matter, we consider the first contact only*/
- contact = contacts->data;
- photo = e_contact_get (contact, E_CONTACT_PHOTO);
- if (!photo)
- photo = e_contact_get (contact, E_CONTACT_LOGO);
- g_list_foreach (contacts, (GFunc)g_object_unref, NULL);
- g_list_free (contacts);
+ photos_cache = g_slist_remove (photos_cache, pi);
+
+ emu_free_photo_info (pi);
}
- stop = stop || camel_operation_cancel_check (NULL);
+ pi = g_new0 (PhotoInfo, 1);
+ pi->address = g_strdup (addr);
+ pi->photo = photo;
- g_object_unref (source); /* Is it? */
- g_object_unref(book);
+ photos_cache = g_slist_append (photos_cache, pi);
}
- g_slist_free(addr_sources);
- e_book_query_unref(query);
+ /* some photo found, use it */
+ if (photo) {
+ /* Form a mime part out of the photo */
+ part = camel_mime_part_new ();
+ camel_mime_part_set_content(part,
+ (const gchar *) photo->data.inlined.data,
+ photo->data.inlined.length, "image/jpeg");
+ }
- if (!photo)
- return NULL;
+ G_UNLOCK (photos_cache);
- if (photo->type != E_CONTACT_PHOTO_TYPE_INLINED) {
- e_contact_photo_free (photo);
- return NULL;
+ return part;
+}
+
+/* list of email addresses (strings) to remove from local cache of photos and contacts,
+ but only if the photo doesn't exist or is an not-found contact */
+void
+emu_remove_from_mail_cache (const GSList *addresses)
+{
+ const GSList *a;
+ GSList *p;
+ CamelInternetAddress *cia;
+
+ cia = camel_internet_address_new ();
+
+ for (a = addresses; a; a = a->next) {
+ const gchar *addr = NULL;
+
+ if (!a->data)
+ continue;
+
+ if (camel_address_decode ((CamelAddress *) cia, a->data) != -1 &&
+ camel_internet_address_get (cia, 0, NULL, &addr) && addr) {
+ gchar *lowercase_addr = g_utf8_strdown (addr, -1);
+
+ G_LOCK (contact_cache);
+ if (g_hash_table_lookup (contact_cache, lowercase_addr) == NOT_FOUND_BOOK)
+ g_hash_table_remove (contact_cache, lowercase_addr);
+ G_UNLOCK (contact_cache);
+
+ g_free (lowercase_addr);
+
+ G_LOCK (photos_cache);
+ for (p = photos_cache; p; p = p->next) {
+ PhotoInfo *pi = p->data;
+
+ if (pi && !pi->photo && g_ascii_strcasecmp (pi->address, addr) == 0) {
+ photos_cache = g_slist_remove (photos_cache, pi);
+ emu_free_photo_info (pi);
+ break;
+ }
+ }
+ G_UNLOCK (photos_cache);
+ }
}
- /* Form a mime part out of the photo */
- part = camel_mime_part_new();
- camel_mime_part_set_content(part,
- (const gchar *) photo->data.inlined.data,
- photo->data.inlined.length, "image/jpeg");
+ camel_object_unref (cia);
+}
- e_contact_photo_free (photo);
+void
+emu_remove_from_mail_cache_1 (const gchar *address)
+{
+ GSList *l;
- return part;
+ g_return_if_fail (address != NULL);
+
+ l = g_slist_append (NULL, (gpointer) address);
+
+ emu_remove_from_mail_cache (l);
+
+ g_slist_free (l);
+}
+
+/* frees all data created by call of em_utils_in_addressbook or em_utils_contact_photo */
+void
+emu_free_mail_cache (void)
+{
+ G_LOCK (contact_cache);
+
+ if (emu_books_hash) {
+ g_hash_table_destroy (emu_books_hash);
+ emu_books_hash = NULL;
+ }
+
+ if (emu_books_source_list) {
+ g_object_unref (emu_books_source_list);
+ emu_books_source_list = NULL;
+ }
+
+ if (contact_cache) {
+ g_hash_table_destroy (contact_cache);
+ contact_cache = NULL;
+ }
+
+ G_UNLOCK (contact_cache);
+
+ G_LOCK (photos_cache);
+
+ g_slist_foreach (photos_cache, (GFunc) emu_free_photo_info, NULL);
+ g_slist_free (photos_cache);
+ photos_cache = NULL;
+
+ G_UNLOCK (photos_cache);
}
void
diff --git a/mail/em-utils.h b/mail/em-utils.h
index 09a1a5dee0..79caba050e 100644
--- a/mail/em-utils.h
+++ b/mail/em-utils.h
@@ -112,6 +112,10 @@ gchar *em_utils_url_unescape_amp (const gchar *url);
struct _EAccount *em_utils_guess_account (CamelMimeMessage *message, CamelFolder *folder);
+void emu_remove_from_mail_cache (const GSList *addresses);
+void emu_remove_from_mail_cache_1 (const gchar *address);
+void emu_free_mail_cache (void);
+
G_END_DECLS
#endif /* __EM_UTILS_H__ */