/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * This program is free software; you can redistribute it and/or * modify it under the terms of version 2 of the GNU General Public * License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "misc/e-gui-utils.h" #include "e-util/e-error.h" #include "e-util/e-icon-factory.h" #include "e-activity-handler.h" #include "mail-config.h" #include "mail-component.h" #include "mail-session.h" #include "mail-mt.h" /*#define MALLOC_CHECK*/ #define LOG_OPS #define LOG_LOCKS #define d(x) static void set_stop(int sensitive); static void mail_operation_status(struct _CamelOperation *op, const char *what, int pc, void *data); #ifdef LOG_LOCKS #define MAIL_MT_LOCK(x) (log_locks?fprintf(log, "%" G_GINT64_MODIFIER "x: lock " # x "\n", e_util_pthread_id(pthread_self())):0, pthread_mutex_lock(&x)) #define MAIL_MT_UNLOCK(x) (log_locks?fprintf(log, "%" G_GINT64_MODIFIER "x: unlock " # x "\n", e_util_pthread_id(pthread_self())): 0, pthread_mutex_unlock(&x)) #else #define MAIL_MT_LOCK(x) pthread_mutex_lock(&x) #define MAIL_MT_UNLOCK(x) pthread_mutex_unlock(&x) #endif /* background operation status stuff */ struct _MailMsgPrivate { int activity_state; /* sigh sigh sigh, we need to keep track of the state external to the pointer itself for locking/race conditions */ int activity_id; GtkWidget *error; gboolean cancelable; }; static GdkPixbuf *progress_icon = NULL; /* mail_msg stuff */ #ifdef LOG_OPS static FILE *log; static int log_ops, log_locks, log_init; #endif static unsigned int mail_msg_seq; /* sequence number of each message */ static GHashTable *mail_msg_active_table; /* table of active messages, must hold mail_msg_lock to access */ static pthread_mutex_t mail_msg_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t mail_msg_cond = PTHREAD_COND_INITIALIZER; MailAsyncEvent *mail_async_event; gpointer mail_msg_new (MailMsgInfo *info) { MailMsg *msg; MAIL_MT_LOCK(mail_msg_lock); #if defined(LOG_OPS) || defined(LOG_LOCKS) if (!log_init) { time_t now = time(NULL); log_init = TRUE; log_ops = getenv("EVOLUTION_MAIL_LOG_OPS") != NULL; log_locks = getenv("EVOLUTION_MAIL_LOG_LOCKS") != NULL; if (log_ops || log_locks) { log = fopen("evolution-mail-ops.log", "w+"); if (log) { setvbuf(log, NULL, _IOLBF, 0); fprintf(log, "Started evolution-mail: %s\n", ctime(&now)); g_warning("Logging mail operations to evolution-mail-ops.log"); if (log_ops) fprintf(log, "Logging async operations\n"); if (log_locks) { fprintf(log, "%" G_GINT64_MODIFIER "x: lock mail_msg_lock\n", e_util_pthread_id(pthread_self())); } } else { g_warning ("Could not open log file: %s", strerror(errno)); log_ops = log_locks = FALSE; } } } #endif msg = g_slice_alloc0 (info->size); msg->info = info; msg->ref_count = 1; msg->seq = mail_msg_seq++; msg->cancel = camel_operation_new(mail_operation_status, GINT_TO_POINTER(msg->seq)); camel_exception_init(&msg->ex); msg->priv = g_slice_new0 (MailMsgPrivate); msg->priv->cancelable = TRUE; g_hash_table_insert(mail_msg_active_table, GINT_TO_POINTER(msg->seq), msg); d(printf("New message %p\n", msg)); #ifdef LOG_OPS if (log_ops) fprintf(log, "%p: New\n", msg); #endif MAIL_MT_UNLOCK(mail_msg_lock); return msg; } static void end_event_callback (CamelObject *o, void *event_data, void *error) { MailComponent *component; EActivityHandler *activity_handler; guint activity_id = GPOINTER_TO_INT (event_data); component = mail_component_peek (); activity_handler = mail_component_peek_activity_handler (component); if (!error) { e_activity_handler_operation_finished (activity_handler, activity_id); } else { d(printf("Yahooooo, we got it nonintrusively\n")); e_activity_handler_operation_set_error (activity_handler, activity_id, error); } } #ifdef MALLOC_CHECK #include static void checkmem(void *p) { if (p) { int status = mprobe(p); switch (status) { case MCHECK_HEAD: printf("Memory underrun at %p\n", p); abort(); case MCHECK_TAIL: printf("Memory overrun at %p\n", p); abort(); case MCHECK_FREE: printf("Double free %p\n", p); abort(); } } } #endif static void mail_msg_free (MailMsg *mail_msg) { if (mail_msg->cancel != NULL) { camel_operation_mute (mail_msg->cancel); camel_operation_unref (mail_msg->cancel); } camel_exception_clear (&mail_msg->ex); g_slice_free (MailMsgPrivate, mail_msg->priv); g_slice_free1 (mail_msg->info->size, mail_msg); } gpointer mail_msg_ref (gpointer msg) { MailMsg *mail_msg = msg; g_return_val_if_fail (mail_msg != NULL, msg); g_return_val_if_fail (mail_msg->ref_count > 0, msg); g_atomic_int_add (&mail_msg->ref_count, 1); return msg; } void mail_msg_unref (gpointer msg) { MailMsg *mail_msg = msg; gint activity_id; GtkWidget *error = NULL; g_return_if_fail (mail_msg != NULL); g_return_if_fail (mail_msg->ref_count > 0); if (g_atomic_int_exchange_and_add (&mail_msg->ref_count, -1) > 1) return; #ifdef MALLOC_CHECK checkmem(mail_msg); checkmem(mail_msg->cancel); checkmem(mail_msg->priv); #endif d(printf("Free message %p\n", msg)); if (mail_msg->info->free) mail_msg->info->free(mail_msg); MAIL_MT_LOCK(mail_msg_lock); #ifdef LOG_OPS if (log_ops) { const gchar *description; description = camel_exception_get_description (&mail_msg->ex); if (description == NULL) description = "None"; fprintf(log, "%p: Free (exception `%s')\n", msg, description); } #endif g_hash_table_remove ( mail_msg_active_table, GINT_TO_POINTER (mail_msg->seq)); pthread_cond_broadcast (&mail_msg_cond); /* We need to make sure we dont lose a reference here YUCK YUCK */ /* This is tightly integrated with the code in do_op_status, as it closely relates to the CamelOperation setup in msg_new() above */ if (mail_msg->priv->activity_state == 1) { /* tell the other to free it itself */ mail_msg->priv->activity_state = 3; MAIL_MT_UNLOCK(mail_msg_lock); return; } else { activity_id = mail_msg->priv->activity_id; error = mail_msg->priv->error; if (error && !activity_id) { e_activity_handler_make_error (mail_component_peek_activity_handler (mail_component_peek ()), "mail", E_LOG_ERROR, error); printf("Making error\n"); } } MAIL_MT_UNLOCK(mail_msg_lock); mail_msg_free (mail_msg); if (activity_id != 0) mail_async_event_emit ( mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) end_event_callback, NULL, GINT_TO_POINTER (activity_id), error); } /* hash table of ops->dialogue of active errors */ static GHashTable *active_errors = NULL; static void error_destroy(GtkObject *o, void *data) { g_hash_table_remove(active_errors, data); } static void error_response(GtkObject *o, int button, void *data) { gtk_widget_destroy((GtkWidget *)o); } void mail_msg_check_error (gpointer msg) { MailMsg *m = msg; char *what; GtkDialog *gd; #ifdef MALLOC_CHECK checkmem(m); checkmem(m->cancel); checkmem(m->priv); #endif /* don't report any errors if we are not in interactive mode */ if (!mail_session_get_interactive ()) return; if (!camel_exception_is_set(&m->ex) || m->ex.id == CAMEL_EXCEPTION_USER_CANCEL || m->ex.id == CAMEL_EXCEPTION_FOLDER_INVALID_UID) return; if (active_errors == NULL) active_errors = g_hash_table_new(NULL, NULL); /* check to see if we have dialogue already running for this operation */ /* we key on the operation pointer, which is at least accurate enough for the operation type, although it could be on a different object. */ if (g_hash_table_lookup(active_errors, m->info)) { g_message("Error occurred while existing dialogue active:\n%s", camel_exception_get_description(&m->ex)); return; } if (m->info->desc && (what = m->info->desc (m))) { gd = (GtkDialog *)e_error_new(NULL, "mail:async-error", what, camel_exception_get_description(&m->ex), NULL); g_free(what); } else gd = (GtkDialog *)e_error_new(NULL, "mail:async-error-nodescribe", camel_exception_get_description(&m->ex), NULL); g_hash_table_insert(active_errors, m->info, gd); g_signal_connect(gd, "response", G_CALLBACK(error_response), m->info); g_signal_connect(gd, "destroy", G_CALLBACK(error_destroy), m->info); if (m->priv->cancelable) m->priv->error = (GtkWidget *) gd; else gtk_widget_show((GtkWidget *)gd); } void mail_msg_cancel(unsigned int msgid) { MailMsg *m; MAIL_MT_LOCK(mail_msg_lock); m = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)); if (m && m->cancel) camel_operation_cancel(m->cancel); MAIL_MT_UNLOCK(mail_msg_lock); } /* waits for a message to be finished processing (freed) the messageid is from MailMsg->seq */ void mail_msg_wait(unsigned int msgid) { MailMsg *m; if (mail_in_main_thread ()) { MAIL_MT_LOCK(mail_msg_lock); m = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)); while (m) { MAIL_MT_UNLOCK(mail_msg_lock); gtk_main_iteration(); MAIL_MT_LOCK(mail_msg_lock); m = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)); } MAIL_MT_UNLOCK(mail_msg_lock); } else { MAIL_MT_LOCK(mail_msg_lock); m = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)); while (m) { pthread_cond_wait(&mail_msg_cond, &mail_msg_lock); m = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)); } MAIL_MT_UNLOCK(mail_msg_lock); } } int mail_msg_active(unsigned int msgid) { int active; MAIL_MT_LOCK(mail_msg_lock); if (msgid == (unsigned int)-1) active = g_hash_table_size(mail_msg_active_table) > 0; else active = g_hash_table_lookup(mail_msg_active_table, GINT_TO_POINTER(msgid)) != NULL; MAIL_MT_UNLOCK(mail_msg_lock); return active; } void mail_msg_wait_all(void) { if (mail_in_main_thread ()) { MAIL_MT_LOCK(mail_msg_lock); while (g_hash_table_size(mail_msg_active_table) > 0) { MAIL_MT_UNLOCK(mail_msg_lock); gtk_main_iteration(); MAIL_MT_LOCK(mail_msg_lock); } MAIL_MT_UNLOCK(mail_msg_lock); } else { MAIL_MT_LOCK(mail_msg_lock); while (g_hash_table_size(mail_msg_active_table) > 0) { pthread_cond_wait(&mail_msg_cond, &mail_msg_lock); } MAIL_MT_UNLOCK(mail_msg_lock); } } /* **************************************** */ static GHookList cancel_hook_list; GHook * mail_cancel_hook_add (GHookFunc func, gpointer data) { GHook *hook; MAIL_MT_LOCK (mail_msg_lock); if (!cancel_hook_list.is_setup) g_hook_list_init (&cancel_hook_list, sizeof (GHook)); hook = g_hook_alloc (&cancel_hook_list); hook->func = func; hook->data = data; g_hook_append (&cancel_hook_list, hook); MAIL_MT_UNLOCK (mail_msg_lock); return hook; } void mail_cancel_hook_remove (GHook *hook) { MAIL_MT_LOCK (mail_msg_lock); g_return_if_fail (cancel_hook_list.is_setup); g_hook_destroy_link (&cancel_hook_list, hook); MAIL_MT_UNLOCK (mail_msg_lock); } void mail_cancel_all (void) { camel_operation_cancel (NULL); MAIL_MT_LOCK (mail_msg_lock); if (cancel_hook_list.is_setup) g_hook_list_invoke (&cancel_hook_list, FALSE); MAIL_MT_UNLOCK (mail_msg_lock); } void mail_msg_set_cancelable (gpointer msg, gboolean status) { MailMsg *mail_msg = msg; mail_msg->priv->cancelable = status; } static guint idle_source_id = 0; G_LOCK_DEFINE_STATIC (idle_source_id); static GAsyncQueue *main_loop_queue = NULL; static GAsyncQueue *msg_reply_queue = NULL; static GThread *main_thread = NULL; static gboolean mail_msg_idle_cb (void) { MailMsg *msg; g_return_val_if_fail (main_loop_queue != NULL, FALSE); g_return_val_if_fail (msg_reply_queue != NULL, FALSE); G_LOCK (idle_source_id); idle_source_id = 0; G_UNLOCK (idle_source_id); /* check the main loop queue */ while ((msg = g_async_queue_try_pop (main_loop_queue)) != NULL) { if (msg->info->exec != NULL) msg->info->exec (msg); if (msg->info->done != NULL) msg->info->done (msg); mail_msg_unref (msg); } /* check the reply queue */ while ((msg = g_async_queue_try_pop (msg_reply_queue)) != NULL) { if (msg->info->done != NULL) msg->info->done (msg); mail_msg_check_error (msg); mail_msg_unref (msg); } return FALSE; } static void mail_msg_proxy (MailMsg *msg) { if (msg->info->desc != NULL) { gchar *text = msg->info->desc (msg); camel_operation_register (msg->cancel); camel_operation_start (msg->cancel, "%s", text); g_free (text); } if (msg->info->exec != NULL) { mail_enable_stop (); msg->info->exec (msg); mail_disable_stop (); } if (msg->info->desc != NULL) { camel_operation_end (msg->cancel); camel_operation_unregister (msg->cancel); MAIL_MT_LOCK (mail_msg_lock); camel_operation_unref (msg->cancel); msg->cancel = NULL; MAIL_MT_UNLOCK (mail_msg_lock); } g_async_queue_push (msg_reply_queue, msg); G_LOCK (idle_source_id); if (idle_source_id == 0) idle_source_id = g_idle_add ( (GSourceFunc) mail_msg_idle_cb, NULL); G_UNLOCK (idle_source_id); } void mail_msg_cleanup (void) { mail_msg_wait_all(); G_LOCK (idle_source_id); if (idle_source_id != 0) { GSource *source; /* Cancel the idle source. */ source = g_main_context_find_source_by_id ( g_main_context_default (), idle_source_id); g_source_destroy (source); idle_source_id = 0; } G_UNLOCK (idle_source_id); g_async_queue_unref (main_loop_queue); main_loop_queue = NULL; g_async_queue_unref (msg_reply_queue); msg_reply_queue = NULL; } void mail_msg_init (void) { main_loop_queue = g_async_queue_new (); msg_reply_queue = g_async_queue_new (); mail_msg_active_table = g_hash_table_new (NULL, NULL); main_thread = g_thread_self (); mail_async_event = mail_async_event_new (); } static gint mail_msg_compare (const MailMsg *msg1, const MailMsg *msg2) { gint priority1 = msg1->priority; gint priority2 = msg2->priority; if (priority1 == priority2) return 0; return (priority1 < priority2) ? 1 : -1; } static gpointer create_thread_pool (gpointer data) { GThreadPool *thread_pool; gint max_threads = GPOINTER_TO_INT (data); /* once created, run forever */ thread_pool = g_thread_pool_new ( (GFunc) mail_msg_proxy, NULL, max_threads, FALSE, NULL); g_thread_pool_set_sort_function ( thread_pool, (GCompareDataFunc) mail_msg_compare, NULL); return thread_pool; } void mail_msg_main_loop_push (gpointer msg) { g_async_queue_push_sorted (main_loop_queue, msg, (GCompareDataFunc) mail_msg_compare, NULL); G_LOCK (idle_source_id); if (idle_source_id == 0) idle_source_id = g_idle_add ( (GSourceFunc) mail_msg_idle_cb, NULL); G_UNLOCK (idle_source_id); } void mail_msg_unordered_push (gpointer msg) { static GOnce once = G_ONCE_INIT; g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (10)); g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL); } void mail_msg_fast_ordered_push (gpointer msg) { static GOnce once = G_ONCE_INIT; g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1)); g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL); } void mail_msg_slow_ordered_push (gpointer msg) { static GOnce once = G_ONCE_INIT; g_once (&once, (GThreadFunc) create_thread_pool, GINT_TO_POINTER (1)); g_thread_pool_push ((GThreadPool *) once.retval, msg, NULL); } gboolean mail_in_main_thread (void) { return (g_thread_self () == main_thread); } /* ********************************************************************** */ /* locks */ static pthread_mutex_t status_lock = PTHREAD_MUTEX_INITIALIZER; /* ********************************************************************** */ struct _proxy_msg { MailMsg base; MailAsyncEvent *ea; mail_async_event_t type; pthread_t thread; int have_thread; MailAsyncFunc func; void *o; void *event_data; void *data; }; static void do_async_event(struct _proxy_msg *m) { m->thread = pthread_self(); m->have_thread = TRUE; m->func(m->o, m->event_data, m->data); m->have_thread = FALSE; g_mutex_lock(m->ea->lock); m->ea->tasks = g_slist_remove(m->ea->tasks, m); g_mutex_unlock(m->ea->lock); } static int idle_async_event(void *mm) { do_async_event(mm); mail_msg_unref(mm); return FALSE; } static MailMsgInfo async_event_info = { sizeof (struct _proxy_msg), (MailMsgDescFunc) NULL, (MailMsgExecFunc) do_async_event, (MailMsgDoneFunc) NULL, (MailMsgFreeFunc) NULL }; MailAsyncEvent *mail_async_event_new(void) { MailAsyncEvent *ea; ea = g_malloc0(sizeof(*ea)); ea->lock = g_mutex_new(); return ea; } int mail_async_event_emit(MailAsyncEvent *ea, mail_async_event_t type, MailAsyncFunc func, void *o, void *event_data, void *data) { struct _proxy_msg *m; int id; /* we dont have a reply port for this, we dont care when/if it gets executed, just queue it */ m = mail_msg_new(&async_event_info); m->func = func; m->o = o; m->event_data = event_data; m->data = data; m->ea = ea; m->type = type; m->have_thread = FALSE; id = m->base.seq; g_mutex_lock(ea->lock); ea->tasks = g_slist_prepend(ea->tasks, m); g_mutex_unlock(ea->lock); /* We use an idle function instead of our own message port only because the gui message ports's notification buffer might overflow and deadlock us */ if (type == MAIL_ASYNC_GUI) { if (mail_in_main_thread ()) g_idle_add(idle_async_event, m); else mail_msg_main_loop_push(m); } else mail_msg_fast_ordered_push (m); return id; } int mail_async_event_destroy(MailAsyncEvent *ea) { int id; pthread_t thread = pthread_self(); struct _proxy_msg *m; g_mutex_lock(ea->lock); while (ea->tasks) { m = ea->tasks->data; id = m->base.seq; if (m->have_thread && pthread_equal(m->thread, thread)) { g_warning("Destroying async event from inside an event, returning EDEADLK"); g_mutex_unlock(ea->lock); errno = EDEADLK; return -1; } g_mutex_unlock(ea->lock); mail_msg_wait(id); g_mutex_lock(ea->lock); } g_mutex_unlock(ea->lock); g_mutex_free(ea->lock); g_free(ea); return 0; } /* ********************************************************************** */ struct _call_msg { MailMsg base; mail_call_t type; MailMainFunc func; void *ret; va_list ap; EFlag *done; }; static void do_call(struct _call_msg *m) { void *p1, *p2, *p3, *p4, *p5; int i1; va_list ap; G_VA_COPY(ap, m->ap); switch(m->type) { case MAIL_CALL_p_p: p1 = va_arg(ap, void *); m->ret = m->func(p1); break; case MAIL_CALL_p_pp: p1 = va_arg(ap, void *); p2 = va_arg(ap, void *); m->ret = m->func(p1, p2); break; case MAIL_CALL_p_ppp: p1 = va_arg(ap, void *); p2 = va_arg(ap, void *); p3 = va_arg(ap, void *); m->ret = m->func(p1, p2, p3); break; case MAIL_CALL_p_pppp: p1 = va_arg(ap, void *); p2 = va_arg(ap, void *); p3 = va_arg(ap, void *); p4 = va_arg(ap, void *); m->ret = m->func(p1, p2, p3, p4); break; case MAIL_CALL_p_ppppp: p1 = va_arg(ap, void *); p2 = va_arg(ap, void *); p3 = va_arg(ap, void *); p4 = va_arg(ap, void *); p5 = va_arg(ap, void *); m->ret = m->func(p1, p2, p3, p4, p5); break; case MAIL_CALL_p_ppippp: p1 = va_arg(ap, void *); p2 = va_arg(ap, void *); i1 = va_arg(ap, int); p3 = va_arg(ap, void *); p4 = va_arg(ap, void *); p5 = va_arg(ap, void *); m->ret = m->func(p1, p2, i1, p3, p4, p5); break; } if (m->done != NULL) e_flag_set (m->done); } static MailMsgInfo mail_call_info = { sizeof (struct _call_msg), (MailMsgDescFunc) NULL, (MailMsgExecFunc) do_call, (MailMsgDoneFunc) NULL, (MailMsgFreeFunc) NULL }; void * mail_call_main (mail_call_t type, MailMainFunc func, ...) { struct _call_msg *m; void *ret; va_list ap; va_start(ap, func); m = mail_msg_new (&mail_call_info); m->type = type; m->func = func; G_VA_COPY(m->ap, ap); if (mail_in_main_thread ()) do_call (m); else { mail_msg_ref (m); m->done = e_flag_new (); mail_msg_main_loop_push (m); e_flag_wait (m->done); e_flag_free (m->done); } va_end(ap); ret = m->ret; mail_msg_unref (m); return ret; } /* ********************************************************************** */ /* locked via status_lock */ static int busy_state; static void do_set_busy(MailMsg *mm) { set_stop(busy_state > 0); } static MailMsgInfo set_busy_info = { sizeof (MailMsg), (MailMsgDescFunc) NULL, (MailMsgExecFunc) do_set_busy, (MailMsgDoneFunc) NULL, (MailMsgFreeFunc) NULL }; void mail_enable_stop(void) { MailMsg *m; MAIL_MT_LOCK(status_lock); busy_state++; if (busy_state == 1) { m = mail_msg_new(&set_busy_info); mail_msg_main_loop_push(m); } MAIL_MT_UNLOCK(status_lock); } void mail_disable_stop(void) { MailMsg *m; MAIL_MT_LOCK(status_lock); busy_state--; if (busy_state == 0) { m = mail_msg_new(&set_busy_info); mail_msg_main_loop_push(m); } MAIL_MT_UNLOCK(status_lock); } /* ******************************************************************************** */ struct _op_status_msg { MailMsg base; struct _CamelOperation *op; char *what; int pc; void *data; }; static void op_status_exec (struct _op_status_msg *m) { EActivityHandler *activity_handler = mail_component_peek_activity_handler (mail_component_peek ()); MailMsg *msg; MailMsgPrivate *data; char *out, *p, *o, c; int pc; g_return_if_fail (mail_in_main_thread ()); MAIL_MT_LOCK (mail_msg_lock); msg = g_hash_table_lookup (mail_msg_active_table, m->data); if (msg == NULL) { MAIL_MT_UNLOCK (mail_msg_lock); return; } data = msg->priv; out = alloca (strlen (m->what) * 2 + 1); o = out; p = m->what; while ((c = *p++)) { if (c == '%') *o++ = '%'; *o++ = c; } *o = 0; pc = m->pc; if (data->activity_id == 0) { char *what; /* its being created/removed? well leave it be */ if (data->activity_state == 1 || data->activity_state == 3) { MAIL_MT_UNLOCK (mail_msg_lock); return; } else { data->activity_state = 1; if (progress_icon == NULL) progress_icon = e_icon_factory_get_icon ("mail-unread", E_ICON_SIZE_MENU); MAIL_MT_UNLOCK (mail_msg_lock); if (msg->info->desc) what = msg->info->desc (msg); else if (m->what) what = g_strdup (m->what); /* uncommenting because message is not very useful for a user, see bug 271734*/ else { what = g_strdup(""); } data->activity_id = e_activity_handler_cancelable_operation_started (activity_handler, "evolution-mail", progress_icon, what, TRUE, (void (*) (gpointer)) camel_operation_cancel, msg->cancel); g_free (what); MAIL_MT_LOCK (mail_msg_lock); if (data->activity_state == 3) { int activity_id = data->activity_id; MAIL_MT_UNLOCK (mail_msg_lock); mail_msg_free (msg); if (activity_id != 0) mail_async_event_emit (mail_async_event, MAIL_ASYNC_GUI, (MailAsyncFunc) end_event_callback, NULL, GINT_TO_POINTER (activity_id), NULL); } else { data->activity_state = 2; MAIL_MT_UNLOCK (mail_msg_lock); } return; } } else if (data->activity_id != 0) { MAIL_MT_UNLOCK (mail_msg_lock); e_activity_handler_operation_progressing (activity_handler, data->activity_id, out, (double)(pc/100.0)); } else { MAIL_MT_UNLOCK (mail_msg_lock); } } static void op_status_free (struct _op_status_msg *m) { g_free (m->what); } static MailMsgInfo op_status_info = { sizeof (struct _op_status_msg), (MailMsgDescFunc) NULL, (MailMsgExecFunc) op_status_exec, (MailMsgDoneFunc) NULL, (MailMsgFreeFunc) op_status_free }; static void mail_operation_status (struct _CamelOperation *op, const char *what, int pc, void *data) { struct _op_status_msg *m; d(printf("got operation statys: %s %d%%\n", what, pc)); m = mail_msg_new(&op_status_info); m->op = op; m->what = g_strdup(what); switch (pc) { case CAMEL_OPERATION_START: pc = 0; break; case CAMEL_OPERATION_END: pc = 100; break; } m->pc = pc; m->data = data; mail_msg_main_loop_push(m); } /* ******************** */ static void set_stop (int sensitive) { static int last = FALSE; if (last == sensitive) return; /*bonobo_ui_component_set_prop (uic, "/commands/MailStop", "sensitive", sensitive ? "1" : "0", NULL);*/ last = sensitive; }