Bug #397265 - Image loading for new contact requires restarting Evolution

This commit is contained in:
Milan Crha
2009-10-26 15:05:27 +05:30
committed by Chenthill Palanisamy
parent ef8bd99497
commit 12cb5975ab
4 changed files with 352 additions and 222 deletions

View File

@ -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);
}

View File

@ -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,249 +1914,376 @@ try_open_e_book (EBook *book, gboolean only_if_exists, GError **error)
return data.result && (!error || !*error);
}
#define NOT_FOUND_BOOK (GINT_TO_POINTER (1))
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
is_local (ESourceGroup *group)
search_address_in_addressbooks (const gchar *address, gboolean local_only, gboolean (*check_contact) (EContact *contact, gpointer user_data), gpointer user_data)
{
return group &&
e_source_group_peek_base_uri (group) &&
g_str_has_prefix (e_source_group_peek_base_uri (group), "file://");
gboolean found = FALSE, stop = FALSE, found_any = FALSE;
gchar *lowercase_addr;
gpointer ptr;
EBookQuery *query;
GSList *s, *g, *addr_sources = NULL;
if (!address || !*address)
return FALSE;
G_LOCK (contact_cache);
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_books_source_list) {
G_UNLOCK (contact_cache);
return FALSE;
}
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, address);
for (g = e_source_list_peek_groups (emu_books_source_list); g; g = g_slist_next (g)) {
ESourceGroup *group = g->data;
if (!group)
continue;
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 (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") == 0) {
addr_sources = g_slist_prepend (addr_sources, g_object_ref (source));
}
}
}
for (s = addr_sources; !stop && !found && s; s = g_slist_next (s)) {
ESource *source = s->data;
GList *contacts;
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)));
hook_book = mail_cancel_hook_add (emu_addr_cancel_book, book);
hook_stop = mail_cancel_hook_add (emu_addr_cancel_stop, &stop);
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;
}
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;
for (l = contacts; l && !found; l = l->next) {
EContact *contact = l->data;
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);
}
mail_cancel_hook_remove (hook_book);
mail_cancel_hook_remove (hook_stop);
stop = stop || camel_operation_cancel_check (NULL);
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_foreach (addr_sources, (GFunc) g_object_unref, NULL);
g_slist_free (addr_sources);
e_book_query_unref (query);
if (!found_any) {
g_hash_table_insert (contact_cache, lowercase_addr, NOT_FOUND_BOOK);
lowercase_addr = NULL;
}
G_UNLOCK (contact_cache);
g_free (lowercase_addr);
return found_any;
}
gboolean
em_utils_in_addressbook (CamelInternetAddress *iaddr, gboolean local_only)
{
GError *err = NULL;
GSList *s, *g, *addr_sources = NULL;
gint stop = FALSE, found = FALSE;
EBookQuery *query;
const gchar *addr;
struct _addr_node *node;
time_t now;
/* TODO: check all addresses? */
if (iaddr == NULL
|| !camel_internet_address_get(iaddr, 0, NULL, &addr))
if (iaddr == NULL || !camel_internet_address_get (iaddr, 0, NULL, &addr))
return FALSE;
pthread_mutex_lock(&emu_addr_lock);
if (emu_addr_cache == NULL) {
mail_call_main(MAIL_CALL_p_p, (MailMainFunc)emu_addr_setup, NULL);
}
if (emu_addr_list == NULL) {
pthread_mutex_unlock(&emu_addr_lock);
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);
}
query = e_book_query_field_test(E_CONTACT_EMAIL, E_BOOK_QUERY_IS, addr);
/* FIXME: this aint threadsafe by any measure, but what can you do eh??? */
for (g = e_source_list_peek_groups(emu_addr_list);g;g=g_slist_next(g)) {
if (local_only && !is_local (g->data))
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 (completion && !g_ascii_strcasecmp (completion, "true")) {
addr_sources = g_slist_prepend(addr_sources, src);
g_object_ref(src);
}
}
}
for (s = addr_sources;!stop && !found && s;s=g_slist_next(s)) {
ESource *source = s->data;
GList *contacts;
EBook *book;
GHook *hook;
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);
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;
}
g_clear_error(&err);
hook = mail_cancel_hook_add(emu_addr_cancel_book, book);
/* 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);
if (err && !stop)
g_warning ("%s: Can't get contacts: %s", G_STRFUNC, err->message);
g_clear_error(&err);
continue;
}
mail_cancel_hook_remove(hook);
if (contacts != NULL) {
found = TRUE;
g_list_foreach(contacts, (GFunc)g_object_unref, NULL);
g_list_free(contacts);
}
stop = stop || camel_operation_cancel_check (NULL);
d(printf(" %s\n", stop?"found":"not found"));
g_object_unref(book);
}
g_slist_free(addr_sources);
if (!stop) {
node->found = found;
node->stamp = now;
}
e_book_query_unref(query);
pthread_mutex_unlock(&emu_addr_lock);
return found;
return search_address_in_addressbooks (addr, local_only, NULL, NULL);
}
CamelMimePart *
em_utils_contact_photo (CamelInternetAddress *cia, gboolean local)
static gboolean
extract_photo_data (EContact *contact, gpointer user_data)
{
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;
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);
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))
/* search a cache first */
for (p = photos_cache; p; p = p->next) {
PhotoInfo *pi = p->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);
photos_cache = g_slist_remove (photos_cache, pi);
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);
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);
if (!photo)
return NULL;
if (photo->type != E_CONTACT_PHOTO_TYPE_INLINED) {
e_contact_photo_free (photo);
return NULL;
}
/* Form a mime part out of the photo */
part = camel_mime_part_new();
camel_mime_part_set_content(part,
/* 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");
}
e_contact_photo_free (photo);
G_UNLOCK (photos_cache);
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);
}
}
camel_object_unref (cia);
}
void
emu_remove_from_mail_cache_1 (const gchar *address)
{
GSList *l;
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
em_utils_clear_get_password_canceled_accounts_flag (void)
{

View File

@ -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__ */

View File

@ -521,6 +521,7 @@ mail_shell_backend_ready_to_quit (EActivity *activity)
{
mail_session_shutdown ();
g_object_unref (activity);
emu_free_mail_cache ();
}
static void