1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize (GObject *obj);
63 static void get_msg_cb (TnyFolder *folder,
69 static void get_msg_status_cb (GObject *obj,
73 static void modest_mail_operation_notify_end (ModestMailOperation *self,
76 static gboolean did_a_cancel = FALSE;
78 enum _ModestMailOperationSignals
80 PROGRESS_CHANGED_SIGNAL,
85 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
86 struct _ModestMailOperationPrivate {
93 ErrorCheckingUserCallback error_checking;
94 gpointer error_checking_user_data;
95 ModestMailOperationStatus status;
96 ModestMailOperationTypeOperation op_type;
99 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
100 MODEST_TYPE_MAIL_OPERATION, \
101 ModestMailOperationPrivate))
103 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
104 priv->status = new_status;\
107 typedef struct _GetMsgAsyncHelper {
108 ModestMailOperation *mail_op;
110 GetMsgAsyncUserCallback user_callback;
114 typedef struct _RefreshAsyncHelper {
115 ModestMailOperation *mail_op;
116 RefreshAsyncUserCallback user_callback;
118 } RefreshAsyncHelper;
120 typedef struct _XFerMsgAsyncHelper
122 ModestMailOperation *mail_op;
124 TnyFolder *dest_folder;
125 XferMsgsAsynUserCallback user_callback;
127 } XFerMsgAsyncHelper;
130 static GObjectClass *parent_class = NULL;
132 static guint signals[NUM_SIGNALS] = {0};
135 modest_mail_operation_get_type (void)
137 static GType my_type = 0;
139 static const GTypeInfo my_info = {
140 sizeof(ModestMailOperationClass),
141 NULL, /* base init */
142 NULL, /* base finalize */
143 (GClassInitFunc) modest_mail_operation_class_init,
144 NULL, /* class finalize */
145 NULL, /* class data */
146 sizeof(ModestMailOperation),
148 (GInstanceInitFunc) modest_mail_operation_init,
151 my_type = g_type_register_static (G_TYPE_OBJECT,
152 "ModestMailOperation",
159 modest_mail_operation_class_init (ModestMailOperationClass *klass)
161 GObjectClass *gobject_class;
162 gobject_class = (GObjectClass*) klass;
164 parent_class = g_type_class_peek_parent (klass);
165 gobject_class->finalize = modest_mail_operation_finalize;
167 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
170 * ModestMailOperation::progress-changed
171 * @self: the #MailOperation that emits the signal
172 * @user_data: user data set when the signal handler was connected
174 * Emitted when the progress of a mail operation changes
176 signals[PROGRESS_CHANGED_SIGNAL] =
177 g_signal_new ("progress-changed",
178 G_TYPE_FROM_CLASS (gobject_class),
180 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
182 g_cclosure_marshal_VOID__POINTER,
183 G_TYPE_NONE, 1, G_TYPE_POINTER);
188 modest_mail_operation_init (ModestMailOperation *obj)
190 ModestMailOperationPrivate *priv;
192 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
194 priv->account = NULL;
195 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
196 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
201 priv->error_checking = NULL;
202 priv->error_checking_user_data = NULL;
206 modest_mail_operation_finalize (GObject *obj)
208 ModestMailOperationPrivate *priv;
210 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
215 g_error_free (priv->error);
219 g_object_unref (priv->source);
223 g_object_unref (priv->account);
224 priv->account = NULL;
228 G_OBJECT_CLASS(parent_class)->finalize (obj);
232 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
235 ModestMailOperation *obj;
236 ModestMailOperationPrivate *priv;
238 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
239 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
241 priv->op_type = op_type;
243 priv->source = g_object_ref(source);
249 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
251 ErrorCheckingUserCallback error_handler,
254 ModestMailOperation *obj;
255 ModestMailOperationPrivate *priv;
257 obj = modest_mail_operation_new (op_type, source);
258 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
260 g_return_val_if_fail (error_handler != NULL, obj);
261 priv->error_checking = error_handler;
267 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
269 ModestMailOperationPrivate *priv;
271 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
272 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
274 if (priv->error_checking != NULL)
275 priv->error_checking (self, priv->error_checking_user_data);
279 ModestMailOperationTypeOperation
280 modest_mail_operation_get_type_operation (ModestMailOperation *self)
282 ModestMailOperationPrivate *priv;
284 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
286 return priv->op_type;
290 modest_mail_operation_is_mine (ModestMailOperation *self,
293 ModestMailOperationPrivate *priv;
295 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
296 if (priv->source == NULL) return FALSE;
298 return priv->source == me;
302 modest_mail_operation_get_source (ModestMailOperation *self)
304 ModestMailOperationPrivate *priv;
306 g_return_val_if_fail (self, NULL);
308 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
310 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
314 return g_object_ref (priv->source);
317 ModestMailOperationStatus
318 modest_mail_operation_get_status (ModestMailOperation *self)
320 ModestMailOperationPrivate *priv;
322 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
323 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
324 MODEST_MAIL_OPERATION_STATUS_INVALID);
326 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
328 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
329 return MODEST_MAIL_OPERATION_STATUS_INVALID;
336 modest_mail_operation_get_error (ModestMailOperation *self)
338 ModestMailOperationPrivate *priv;
340 g_return_val_if_fail (self, NULL);
341 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
343 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
346 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
354 modest_mail_operation_cancel (ModestMailOperation *self)
356 ModestMailOperationPrivate *priv;
358 if (!MODEST_IS_MAIL_OPERATION (self)) {
359 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
363 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
365 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
372 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
374 /* This emits progress-changed on which the mail operation queue is
375 * listening, so the mail operation is correctly removed from the
376 * queue without further explicit calls. */
377 modest_mail_operation_notify_end (self, FALSE);
383 modest_mail_operation_get_task_done (ModestMailOperation *self)
385 ModestMailOperationPrivate *priv;
387 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
389 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
394 modest_mail_operation_get_task_total (ModestMailOperation *self)
396 ModestMailOperationPrivate *priv;
398 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
400 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
405 modest_mail_operation_is_finished (ModestMailOperation *self)
407 ModestMailOperationPrivate *priv;
408 gboolean retval = FALSE;
410 if (!MODEST_IS_MAIL_OPERATION (self)) {
411 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
415 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
417 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
418 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
420 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
430 modest_mail_operation_get_id (ModestMailOperation *self)
432 ModestMailOperationPrivate *priv;
434 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
436 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
441 modest_mail_operation_set_id (ModestMailOperation *self,
444 ModestMailOperationPrivate *priv;
446 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
448 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
453 * Creates an image of the current state of a mail operation, the
454 * caller must free it
456 static ModestMailOperationState *
457 modest_mail_operation_clone_state (ModestMailOperation *self)
459 ModestMailOperationState *state;
460 ModestMailOperationPrivate *priv;
462 /* FIXME: this should be fixed properly
464 * in some cases, priv was NULL, so checking here to
467 g_return_val_if_fail (self, NULL);
468 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
469 g_return_val_if_fail (priv, NULL);
474 state = g_slice_new (ModestMailOperationState);
476 state->status = priv->status;
477 state->op_type = priv->op_type;
478 state->done = priv->done;
479 state->total = priv->total;
480 state->finished = modest_mail_operation_is_finished (self);
481 state->bytes_done = 0;
482 state->bytes_total = 0;
487 /* ******************************************************************* */
488 /* ************************** SEND ACTIONS ************************* */
489 /* ******************************************************************* */
492 modest_mail_operation_send_mail (ModestMailOperation *self,
493 TnyTransportAccount *transport_account,
496 TnySendQueue *send_queue = NULL;
497 ModestMailOperationPrivate *priv;
499 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
500 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
501 g_return_if_fail (TNY_IS_MSG (msg));
503 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
505 /* Get account and set it into mail_operation */
506 priv->account = g_object_ref (transport_account);
510 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
511 if (!TNY_IS_SEND_QUEUE(send_queue)) {
512 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
513 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
514 "modest: could not find send queue for account\n");
516 /* TODO: connect to the msg-sent in order to know when
517 the mail operation is finished */
518 tny_send_queue_add (send_queue, msg, &(priv->error));
519 /* TODO: we're setting always success, do the check in
521 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
524 /* TODO: do this in the handler of the "msg-sent"
525 signal.Notify about operation end */
526 modest_mail_operation_notify_end (self, FALSE);
530 modest_mail_operation_send_new_mail (ModestMailOperation *self,
531 TnyTransportAccount *transport_account,
533 const gchar *from, const gchar *to,
534 const gchar *cc, const gchar *bcc,
535 const gchar *subject, const gchar *plain_body,
536 const gchar *html_body,
537 const GList *attachments_list,
538 TnyHeaderFlags priority_flags)
540 TnyMsg *new_msg = NULL;
541 TnyFolder *folder = NULL;
542 TnyHeader *header = NULL;
543 ModestMailOperationPrivate *priv = NULL;
545 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
546 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
548 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
550 /* Check parametters */
552 /* Set status failed and set an error */
553 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
554 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
555 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
556 _("Error trying to send a mail. You need to set at least one recipient"));
560 if (html_body == NULL) {
561 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
563 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
566 g_printerr ("modest: failed to create a new msg\n");
570 /* Set priority flags in message */
571 header = tny_msg_get_header (new_msg);
572 if (priority_flags != 0)
573 tny_header_set_flags (header, priority_flags);
575 /* Call mail operation */
576 modest_mail_operation_send_mail (self, transport_account, new_msg);
578 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
580 if (draft_msg != NULL) {
581 header = tny_msg_get_header (draft_msg);
582 /* Note: This can fail (with a warning) if the message is not really already in a folder,
583 * because this function requires it to have a UID. */
584 tny_folder_remove_msg (folder, header, NULL);
585 g_object_unref (header);
590 g_object_unref (G_OBJECT (new_msg));
594 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
595 TnyTransportAccount *transport_account,
597 const gchar *from, const gchar *to,
598 const gchar *cc, const gchar *bcc,
599 const gchar *subject, const gchar *plain_body,
600 const gchar *html_body,
601 const GList *attachments_list,
602 TnyHeaderFlags priority_flags)
605 TnyFolder *folder = NULL;
606 TnyHeader *header = NULL;
607 ModestMailOperationPrivate *priv = NULL;
609 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
610 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
612 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
614 /* Get account and set it into mail_operation */
615 priv->account = g_object_ref (transport_account);
617 if (html_body == NULL) {
618 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
620 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
623 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
624 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
625 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
626 "modest: failed to create a new msg\n");
630 /* add priority flags */
631 header = tny_msg_get_header (msg);
632 tny_header_set_flags (header, priority_flags);
634 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
636 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
637 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
638 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
639 "modest: failed to create a new msg\n");
643 if (draft_msg != NULL) {
644 header = tny_msg_get_header (draft_msg);
645 /* Remove the old draft expunging it */
646 tny_folder_remove_msg (folder, header, NULL);
647 tny_folder_sync (folder, TRUE, &(priv->error));
648 g_object_unref (header);
652 tny_folder_add_msg (folder, msg, &(priv->error));
655 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
657 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
661 g_object_unref (G_OBJECT(msg));
663 g_object_unref (G_OBJECT(folder));
665 modest_mail_operation_notify_end (self, FALSE);
670 ModestMailOperation *mail_op;
671 TnyStoreAccount *account;
672 TnyTransportAccount *transport_account;
675 gchar *retrieve_type;
677 UpdateAccountCallback callback;
681 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
682 /* We use this folder observer to track the headers that have been
683 * added to a folder */
686 TnyList *new_headers;
687 } InternalFolderObserver;
691 } InternalFolderObserverClass;
693 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
695 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
696 internal_folder_observer,
698 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
702 foreach_add_item (gpointer header, gpointer user_data)
704 /* printf("DEBUG: %s: header subject=%s\n",
705 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
707 tny_list_prepend (TNY_LIST (user_data),
708 g_object_ref (G_OBJECT (header)));
711 /* This is the method that looks for new messages in a folder */
713 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
715 InternalFolderObserver *derived = (InternalFolderObserver *)self;
717 TnyFolderChangeChanged changed;
719 changed = tny_folder_change_get_changed (change);
721 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
724 /* Get added headers */
725 list = tny_simple_list_new ();
726 tny_folder_change_get_added_headers (change, list);
728 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
729 * __FUNCTION__, tny_list_get_length(list));
732 /* Add them to the folder observer */
733 tny_list_foreach (list, foreach_add_item,
734 derived->new_headers);
736 g_object_unref (G_OBJECT (list));
741 internal_folder_observer_init (InternalFolderObserver *self)
743 self->new_headers = tny_simple_list_new ();
746 internal_folder_observer_finalize (GObject *object)
748 InternalFolderObserver *self;
750 self = (InternalFolderObserver *) object;
751 g_object_unref (self->new_headers);
753 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
756 tny_folder_observer_init (TnyFolderObserverIface *iface)
758 iface->update_func = internal_folder_observer_update;
761 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
763 GObjectClass *object_class;
765 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
766 object_class = (GObjectClass*) klass;
767 object_class->finalize = internal_folder_observer_finalize;
773 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
776 TnyList *folders = tny_simple_list_new ();
778 tny_folder_store_get_folders (store, folders, query, NULL);
779 iter = tny_list_create_iterator (folders);
781 while (!tny_iterator_is_done (iter)) {
783 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
785 tny_list_prepend (all_folders, G_OBJECT (folder));
786 recurse_folders (folder, query, all_folders);
787 g_object_unref (G_OBJECT (folder));
789 tny_iterator_next (iter);
791 g_object_unref (G_OBJECT (iter));
792 g_object_unref (G_OBJECT (folders));
796 * Issues the "progress-changed" signal. The timer won't be removed,
797 * so you must call g_source_remove to stop the signal emission
800 idle_notify_progress (gpointer data)
802 gdk_threads_enter ();
804 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
805 ModestMailOperationState *state;
807 state = modest_mail_operation_clone_state (mail_op);
808 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
809 g_slice_free (ModestMailOperationState, state);
811 gdk_threads_leave ();
817 * Issues the "progress-changed" signal and removes the timer. It uses
818 * a lock to ensure that the progress information of the mail
819 * operation is not modified while there are notifications pending
822 idle_notify_progress_once (gpointer data)
824 gdk_threads_enter ();
828 pair = (ModestPair *) data;
830 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
832 /* Free the state and the reference to the mail operation */
833 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
834 g_object_unref (pair->first);
836 gdk_threads_leave ();
842 * Used by update_account_thread to notify the queue from the main
843 * loop. We call it inside an idle call to achieve that
846 idle_notify_update_account_queue (gpointer data)
848 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
849 ModestMailOperationPrivate *priv = NULL;
851 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
853 /* Do not need to block, the notify end will do it for us */
854 modest_mail_operation_notify_end (mail_op, TRUE);
855 g_object_unref (mail_op);
861 compare_headers_by_date (gconstpointer a,
864 TnyHeader **header1, **header2;
867 header1 = (TnyHeader **) a;
868 header2 = (TnyHeader **) b;
870 sent1 = tny_header_get_date_sent (*header1);
871 sent2 = tny_header_get_date_sent (*header2);
873 /* We want the most recent ones (greater time_t) at the
882 set_last_updated_idle (gpointer data)
884 gdk_threads_enter ();
886 /* It does not matter if the time is not exactly the same than
887 the time when this idle was called, it's just an
888 approximation and it won't be very different */
889 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
891 MODEST_ACCOUNT_LAST_UPDATED,
895 gdk_threads_leave ();
901 update_account_thread (gpointer thr_user_data)
903 static gboolean first_time = TRUE;
904 UpdateAccountInfo *info;
905 TnyList *all_folders = NULL;
906 GPtrArray *new_headers = NULL;
907 TnyIterator *iter = NULL;
908 TnyFolderStoreQuery *query = NULL;
909 ModestMailOperationPrivate *priv = NULL;
910 ModestTnySendQueue *send_queue = NULL;
912 info = (UpdateAccountInfo *) thr_user_data;
913 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
915 /* Get account and set it into mail_operation */
916 priv->account = g_object_ref (info->account);
919 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
920 * show any updates unless we do that
922 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
923 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
925 /* Get all the folders. We can do it synchronously because
926 we're already running in a different thread than the UI */
927 all_folders = tny_simple_list_new ();
928 query = tny_folder_store_query_new ();
929 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
930 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
935 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
939 iter = tny_list_create_iterator (all_folders);
940 while (!tny_iterator_is_done (iter)) {
941 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
943 recurse_folders (folder, query, all_folders);
944 tny_iterator_next (iter);
946 g_object_unref (G_OBJECT (iter));
948 /* Update status and notify. We need to call the notification
949 with a source function in order to call it from the main
950 loop. We need that in order not to get into trouble with
951 Gtk+. We use a timeout in order to provide more status
952 information, because the sync tinymail call does not
953 provide it for the moment */
954 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
956 /* Refresh folders */
957 new_headers = g_ptr_array_new ();
958 iter = tny_list_create_iterator (all_folders);
960 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
962 InternalFolderObserver *observer;
963 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
965 /* Refresh the folder */
966 /* Our observer receives notification of new emails during folder refreshes,
967 * so we can use observer->new_headers.
969 observer = g_object_new (internal_folder_observer_get_type (), NULL);
970 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
972 /* This gets the status information (headers) from the server.
973 * We use the blocking version, because we are already in a separate
977 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
978 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
981 /* If the retrieve type is full messages, refresh and get the messages */
982 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
984 iter = tny_list_create_iterator (observer->new_headers);
985 while (!tny_iterator_is_done (iter)) {
986 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
988 /* Apply per-message size limits */
989 if (tny_header_get_message_size (header) < info->max_size)
990 g_ptr_array_add (new_headers, g_object_ref (header));
992 g_object_unref (header);
993 tny_iterator_next (iter);
995 g_object_unref (iter);
997 /* We do not need to do it the first time
998 because it's automatically done by the tree
1000 if (G_UNLIKELY (!first_time))
1001 tny_folder_poke_status (TNY_FOLDER (folder));
1003 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1004 g_object_unref (observer);
1007 g_object_unref (G_OBJECT (folder));
1010 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1014 tny_iterator_next (iter);
1017 did_a_cancel = FALSE;
1019 g_object_unref (G_OBJECT (iter));
1020 g_source_remove (timeout);
1022 if (new_headers->len > 0) {
1026 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1028 /* Apply message count limit */
1029 /* If the number of messages exceeds the maximum, ask the
1030 * user to download them all,
1031 * as per the UI spec "Retrieval Limits" section in 4.4:
1033 if (new_headers->len > info->retrieve_limit) {
1034 /* TODO: Ask the user, instead of just
1036 * mail_nc_msg_count_limit_exceeded, with 'Get
1037 * all' and 'Newest only' buttons. */
1038 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1039 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1040 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1041 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1042 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1047 priv->total = MIN (new_headers->len, info->retrieve_limit);
1048 while (msg_num < priv->total) {
1050 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1051 TnyFolder *folder = tny_header_get_folder (header);
1052 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1053 ModestMailOperationState *state;
1057 /* We can not just use the mail operation because the
1058 values of done and total could change before the
1060 state = modest_mail_operation_clone_state (info->mail_op);
1061 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1062 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1063 pair, (GDestroyNotify) modest_pair_free);
1065 g_object_unref (msg);
1066 g_object_unref (folder);
1070 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1071 g_ptr_array_free (new_headers, FALSE);
1075 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1078 if (priv->account != NULL)
1079 g_object_unref (priv->account);
1080 priv->account = g_object_ref (info->transport_account);
1082 send_queue = modest_runtime_get_send_queue (info->transport_account);
1084 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1085 modest_tny_send_queue_try_to_send (send_queue);
1086 g_source_remove (timeout);
1088 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1089 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1090 "cannot create a send queue for %s\n",
1091 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1092 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1095 /* Check if the operation was a success */
1097 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1099 /* Update the last updated key */
1100 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1101 set_last_updated_idle,
1102 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1103 (GDestroyNotify) g_free);
1107 /* Notify about operation end. Note that the info could be
1108 freed before this idle happens, but the mail operation will
1110 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1112 if (info->callback) {
1113 /* This thread is not in the main lock */
1114 gdk_threads_enter ();
1115 info->callback (info->mail_op,
1116 (new_headers) ? new_headers->len : 0,
1118 gdk_threads_leave ();
1122 g_object_unref (query);
1123 g_object_unref (all_folders);
1124 g_object_unref (info->account);
1125 g_object_unref (info->transport_account);
1126 g_free (info->retrieve_type);
1127 g_slice_free (UpdateAccountInfo, info);
1135 modest_mail_operation_update_account (ModestMailOperation *self,
1136 const gchar *account_name,
1137 UpdateAccountCallback callback,
1141 UpdateAccountInfo *info;
1142 ModestMailOperationPrivate *priv;
1143 ModestAccountMgr *mgr;
1144 TnyStoreAccount *modest_account;
1145 TnyTransportAccount *transport_account;
1147 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1148 g_return_val_if_fail (account_name, FALSE);
1150 /* Init mail operation. Set total and done to 0, and do not
1151 update them, this way the progress objects will know that
1152 we have no clue about the number of the objects */
1153 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1156 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1158 /* Make sure that we have a connection, and request one
1160 * TODO: Is there some way to trigger this for every attempt to
1161 * use the network? */
1162 if (!modest_platform_connect_and_wait(NULL))
1165 /* Get the Modest account */
1166 modest_account = (TnyStoreAccount *)
1167 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1169 TNY_ACCOUNT_TYPE_STORE);
1171 if (!modest_account) {
1172 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1173 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1174 "cannot get tny store account for %s\n", account_name);
1179 /* Get the transport account, we can not do it in the thread
1180 due to some problems with dbus */
1181 transport_account = (TnyTransportAccount *)
1182 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1184 if (!transport_account) {
1185 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1186 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1187 "cannot get tny transport account for %s\n", account_name);
1191 /* Create the helper object */
1192 info = g_slice_new (UpdateAccountInfo);
1193 info->mail_op = self;
1194 info->account = modest_account;
1195 info->transport_account = transport_account;
1196 info->callback = callback;
1197 info->user_data = user_data;
1199 /* Get the message size limit */
1200 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1201 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1202 if (info->max_size == 0)
1203 info->max_size = G_MAXINT;
1205 info->max_size = info->max_size * KB;
1207 /* Get per-account retrieval type */
1208 mgr = modest_runtime_get_account_mgr ();
1209 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1210 MODEST_ACCOUNT_RETRIEVE, FALSE);
1212 /* Get per-account message amount retrieval limit */
1213 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1214 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1215 if (info->retrieve_limit == 0)
1216 info->retrieve_limit = G_MAXINT;
1218 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1220 /* Set account busy */
1221 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1222 priv->account_name = g_strdup(account_name);
1224 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1229 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1231 callback (self, 0, user_data);
1232 modest_mail_operation_notify_end (self, FALSE);
1236 /* ******************************************************************* */
1237 /* ************************** STORE ACTIONS ************************* */
1238 /* ******************************************************************* */
1242 modest_mail_operation_create_folder (ModestMailOperation *self,
1243 TnyFolderStore *parent,
1246 ModestMailOperationPrivate *priv;
1247 TnyFolder *new_folder = NULL;
1249 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1250 g_return_val_if_fail (name, NULL);
1252 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1255 if (TNY_IS_FOLDER (parent)) {
1256 /* Check folder rules */
1257 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1258 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1259 /* Set status failed and set an error */
1260 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1261 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1262 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1263 _("mail_in_ui_folder_create_error"));
1267 if (!strcmp (name, " ") || strchr (name, '/')) {
1268 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1269 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1270 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1271 _("mail_in_ui_folder_create_error"));
1275 /* Create the folder */
1276 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1277 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1279 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1282 /* Notify about operation end */
1283 modest_mail_operation_notify_end (self, FALSE);
1289 modest_mail_operation_remove_folder (ModestMailOperation *self,
1291 gboolean remove_to_trash)
1293 TnyAccount *account;
1294 ModestMailOperationPrivate *priv;
1295 ModestTnyFolderRules rules;
1297 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1298 g_return_if_fail (TNY_IS_FOLDER (folder));
1300 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1302 /* Check folder rules */
1303 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1304 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1305 /* Set status failed and set an error */
1306 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1307 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1308 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1309 _("mail_in_ui_folder_delete_error"));
1313 /* Get the account */
1314 account = modest_tny_folder_get_account (folder);
1315 priv->account = g_object_ref(account);
1317 /* Delete folder or move to trash */
1318 if (remove_to_trash) {
1319 TnyFolder *trash_folder = NULL;
1320 trash_folder = modest_tny_account_get_special_folder (account,
1321 TNY_FOLDER_TYPE_TRASH);
1322 /* TODO: error_handling */
1323 modest_mail_operation_xfer_folder (self, folder,
1324 TNY_FOLDER_STORE (trash_folder), TRUE);
1326 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1328 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1329 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1332 g_object_unref (G_OBJECT (parent));
1334 g_object_unref (G_OBJECT (account));
1337 /* Notify about operation end */
1338 modest_mail_operation_notify_end (self, FALSE);
1342 transfer_folder_status_cb (GObject *obj,
1346 ModestMailOperation *self;
1347 ModestMailOperationPrivate *priv;
1348 ModestMailOperationState *state;
1350 g_return_if_fail (status != NULL);
1351 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1353 self = MODEST_MAIL_OPERATION (user_data);
1354 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1356 if ((status->position == 1) && (status->of_total == 100))
1359 priv->done = status->position;
1360 priv->total = status->of_total;
1362 state = modest_mail_operation_clone_state (self);
1363 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1364 g_slice_free (ModestMailOperationState, state);
1369 transfer_folder_cb (TnyFolder *folder,
1370 TnyFolderStore *into,
1372 TnyFolder *new_folder,
1376 ModestMailOperation *self = NULL;
1377 ModestMailOperationPrivate *priv = NULL;
1379 self = MODEST_MAIL_OPERATION (user_data);
1381 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1384 priv->error = g_error_copy (*err);
1386 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1387 } else if (cancelled) {
1388 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1389 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1390 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1391 _("Transference of %s was cancelled."),
1392 tny_folder_get_name (folder));
1395 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1399 g_object_unref (folder);
1400 g_object_unref (into);
1402 /* Notify about operation end */
1403 modest_mail_operation_notify_end (self, TRUE);
1407 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1409 TnyFolderStore *parent,
1410 gboolean delete_original)
1412 ModestMailOperationPrivate *priv = NULL;
1413 ModestTnyFolderRules parent_rules, rules;
1415 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1416 g_return_if_fail (TNY_IS_FOLDER (folder));
1417 g_return_if_fail (TNY_IS_FOLDER (parent));
1419 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1421 /* Get account and set it into mail_operation */
1422 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1423 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1425 /* Get folder rules */
1426 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1427 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1429 if (!TNY_IS_FOLDER_STORE (parent)) {
1433 /* The moveable restriction is applied also to copy operation */
1434 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1435 /* Set status failed and set an error */
1436 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1437 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1438 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1439 _("mail_in_ui_folder_move_target_error"));
1441 /* Notify the queue */
1442 modest_mail_operation_notify_end (self, FALSE);
1443 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1444 /* Set status failed and set an error */
1445 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1446 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1447 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1448 _("FIXME: parent folder does not accept new folders"));
1450 /* Notify the queue */
1451 modest_mail_operation_notify_end (self, FALSE);
1453 /* Pick references for async calls */
1454 g_object_ref (folder);
1455 g_object_ref (parent);
1457 /* Move/Copy folder */
1458 tny_folder_copy_async (folder,
1460 tny_folder_get_name (folder),
1463 transfer_folder_status_cb,
1469 modest_mail_operation_rename_folder (ModestMailOperation *self,
1473 ModestMailOperationPrivate *priv;
1474 ModestTnyFolderRules rules;
1476 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1477 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1478 g_return_if_fail (name);
1480 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1482 /* Get account and set it into mail_operation */
1483 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1485 /* Check folder rules */
1486 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1487 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1488 /* Set status failed and set an error */
1489 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1490 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1491 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1492 _("FIXME: unable to rename"));
1494 /* Notify about operation end */
1495 modest_mail_operation_notify_end (self, FALSE);
1496 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1497 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1498 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1499 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1500 _("FIXME: unable to rename"));
1501 /* Notify about operation end */
1502 modest_mail_operation_notify_end (self, FALSE);
1504 TnyFolderStore *into;
1506 /* Rename. Camel handles folder subscription/unsubscription */
1507 into = tny_folder_get_folder_store (folder);
1508 tny_folder_copy_async (folder, into, name, TRUE,
1510 transfer_folder_status_cb,
1513 g_object_unref (into);
1517 /* ******************************************************************* */
1518 /* ************************** MSG ACTIONS ************************* */
1519 /* ******************************************************************* */
1521 void modest_mail_operation_get_msg (ModestMailOperation *self,
1523 GetMsgAsyncUserCallback user_callback,
1526 GetMsgAsyncHelper *helper = NULL;
1528 ModestMailOperationPrivate *priv;
1530 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1531 g_return_if_fail (TNY_IS_HEADER (header));
1533 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1534 folder = tny_header_get_folder (header);
1536 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1538 /* Get message from folder */
1540 /* Get account and set it into mail_operation */
1541 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1543 helper = g_slice_new0 (GetMsgAsyncHelper);
1544 helper->mail_op = self;
1545 helper->user_callback = user_callback;
1546 helper->user_data = user_data;
1547 helper->header = g_object_ref (header);
1549 // The callback's reference so that the mail op is not
1550 // finalized until the async operation is completed even if
1551 // the user canceled the request meanwhile.
1552 g_object_ref (G_OBJECT (helper->mail_op));
1554 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1556 g_object_unref (G_OBJECT (folder));
1558 /* Set status failed and set an error */
1559 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1560 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1561 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1562 _("Error trying to get a message. No folder found for header"));
1564 /* Notify the queue */
1565 modest_mail_operation_notify_end (self, FALSE);
1570 get_msg_cb (TnyFolder *folder,
1576 GetMsgAsyncHelper *helper = NULL;
1577 ModestMailOperation *self = NULL;
1578 ModestMailOperationPrivate *priv = NULL;
1580 helper = (GetMsgAsyncHelper *) user_data;
1581 g_return_if_fail (helper != NULL);
1582 self = helper->mail_op;
1583 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1584 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1586 /* Check errors and cancel */
1588 priv->error = g_error_copy (*error);
1589 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1593 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1594 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1595 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1596 _("Error trying to refresh the contents of %s"),
1597 tny_folder_get_name (folder));
1601 /* The mail operation might have been canceled in which case we do not
1602 want to notify anyone anymore. */
1603 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1604 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1606 /* If user defined callback function was defined, call it */
1607 if (helper->user_callback) {
1608 /* This callback is called into an iddle by tinymail,
1609 and idles are not in the main lock */
1610 gdk_threads_enter ();
1611 helper->user_callback (self, helper->header, msg, helper->user_data);
1612 gdk_threads_leave ();
1618 g_object_unref (helper->header);
1619 g_slice_free (GetMsgAsyncHelper, helper);
1621 /* Notify about operation end */
1622 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1623 modest_mail_operation_notify_end (self, TRUE);
1625 g_object_unref (G_OBJECT (self));
1629 get_msg_status_cb (GObject *obj,
1633 GetMsgAsyncHelper *helper = NULL;
1634 ModestMailOperation *self;
1635 ModestMailOperationPrivate *priv;
1636 ModestMailOperationState *state;
1638 g_return_if_fail (status != NULL);
1639 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1641 helper = (GetMsgAsyncHelper *) user_data;
1642 g_return_if_fail (helper != NULL);
1644 self = helper->mail_op;
1645 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1647 if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1650 if ((status->position == 1) && (status->of_total == 100))
1656 state = modest_mail_operation_clone_state (self);
1657 state->bytes_done = status->position;
1658 state->bytes_total = status->of_total;
1659 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1660 g_slice_free (ModestMailOperationState, state);
1663 /****************************************************/
1665 ModestMailOperation *mail_op;
1667 GetMsgAsyncUserCallback user_callback;
1669 GDestroyNotify notify;
1673 GetMsgAsyncUserCallback user_callback;
1677 ModestMailOperation *mail_op;
1678 } NotifyGetMsgsInfo;
1682 * Used by get_msgs_full_thread to call the user_callback for each
1683 * message that has been read
1686 notify_get_msgs_full (gpointer data)
1688 NotifyGetMsgsInfo *info;
1690 info = (NotifyGetMsgsInfo *) data;
1692 /* Call the user callback. Idles are not in the main lock, so
1694 gdk_threads_enter ();
1695 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1696 gdk_threads_leave ();
1698 g_slice_free (NotifyGetMsgsInfo, info);
1704 * Used by get_msgs_full_thread to free al the thread resources and to
1705 * call the destroy function for the passed user_data
1708 get_msgs_full_destroyer (gpointer data)
1710 GetFullMsgsInfo *info;
1712 info = (GetFullMsgsInfo *) data;
1715 gdk_threads_enter ();
1716 info->notify (info->user_data);
1717 gdk_threads_leave ();
1721 g_object_unref (info->headers);
1722 g_slice_free (GetFullMsgsInfo, info);
1728 get_msgs_full_thread (gpointer thr_user_data)
1730 GetFullMsgsInfo *info;
1731 ModestMailOperationPrivate *priv = NULL;
1732 TnyIterator *iter = NULL;
1734 info = (GetFullMsgsInfo *) thr_user_data;
1735 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1737 iter = tny_list_create_iterator (info->headers);
1738 while (!tny_iterator_is_done (iter)) {
1742 header = TNY_HEADER (tny_iterator_get_current (iter));
1743 folder = tny_header_get_folder (header);
1745 /* Get message from folder */
1748 /* The callback will call it per each header */
1749 msg = tny_folder_get_msg (folder, header, &(priv->error));
1752 ModestMailOperationState *state;
1757 /* notify progress */
1758 state = modest_mail_operation_clone_state (info->mail_op);
1759 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1760 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1761 pair, (GDestroyNotify) modest_pair_free);
1763 /* The callback is the responsible for
1764 freeing the message */
1765 if (info->user_callback) {
1766 NotifyGetMsgsInfo *info_notify;
1767 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1768 info_notify->user_callback = info->user_callback;
1769 info_notify->mail_op = info->mail_op;
1770 info_notify->header = g_object_ref (header);
1771 info_notify->msg = g_object_ref (msg);
1772 info_notify->user_data = info->user_data;
1773 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1774 notify_get_msgs_full,
1777 g_object_unref (msg);
1780 /* Set status failed and set an error */
1781 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1782 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1783 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1784 "Error trying to get a message. No folder found for header");
1786 g_object_unref (header);
1787 tny_iterator_next (iter);
1790 /* Set operation status */
1791 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1792 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1794 /* Notify about operation end */
1795 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1797 /* Free thread resources. Will be called after all previous idles */
1798 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1804 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1805 TnyList *header_list,
1806 GetMsgAsyncUserCallback user_callback,
1808 GDestroyNotify notify)
1810 TnyHeader *header = NULL;
1811 TnyFolder *folder = NULL;
1813 ModestMailOperationPrivate *priv = NULL;
1814 GetFullMsgsInfo *info = NULL;
1815 gboolean size_ok = TRUE;
1817 TnyIterator *iter = NULL;
1819 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1821 /* Init mail operation */
1822 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1823 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1825 priv->total = tny_list_get_length(header_list);
1827 /* Get account and set it into mail_operation */
1828 if (tny_list_get_length (header_list) >= 1) {
1829 iter = tny_list_create_iterator (header_list);
1830 header = TNY_HEADER (tny_iterator_get_current (iter));
1831 folder = tny_header_get_folder (header);
1832 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1833 g_object_unref (header);
1834 g_object_unref (folder);
1836 if (tny_list_get_length (header_list) == 1) {
1837 g_object_unref (iter);
1842 /* Get msg size limit */
1843 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1844 MODEST_CONF_MSG_SIZE_LIMIT,
1847 g_clear_error (&(priv->error));
1848 max_size = G_MAXINT;
1850 max_size = max_size * KB;
1853 /* Check message size limits. If there is only one message
1854 always retrieve it */
1856 while (!tny_iterator_is_done (iter) && size_ok) {
1857 header = TNY_HEADER (tny_iterator_get_current (iter));
1858 if (tny_header_get_message_size (header) >= max_size)
1860 g_object_unref (header);
1861 tny_iterator_next (iter);
1863 g_object_unref (iter);
1867 /* Create the info */
1868 info = g_slice_new0 (GetFullMsgsInfo);
1869 info->mail_op = self;
1870 info->user_callback = user_callback;
1871 info->user_data = user_data;
1872 info->headers = g_object_ref (header_list);
1873 info->notify = notify;
1875 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1877 /* Set status failed and set an error */
1878 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1879 /* FIXME: the error msg is different for pop */
1880 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1881 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1882 _("emev_ni_ui_imap_msg_size_exceed_error"));
1883 /* Remove from queue and free resources */
1884 modest_mail_operation_notify_end (self, FALSE);
1892 modest_mail_operation_remove_msg (ModestMailOperation *self,
1894 gboolean remove_to_trash)
1897 ModestMailOperationPrivate *priv;
1899 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1900 g_return_if_fail (TNY_IS_HEADER (header));
1902 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1903 folder = tny_header_get_folder (header);
1905 /* Get account and set it into mail_operation */
1906 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1908 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1910 /* Delete or move to trash */
1911 if (remove_to_trash) {
1912 TnyFolder *trash_folder;
1913 TnyStoreAccount *store_account;
1915 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1916 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1917 TNY_FOLDER_TYPE_TRASH);
1922 headers = tny_simple_list_new ();
1923 tny_list_append (headers, G_OBJECT (header));
1924 g_object_unref (header);
1927 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1928 g_object_unref (headers);
1929 /* g_object_unref (trash_folder); */
1931 ModestMailOperationPrivate *priv;
1933 /* Set status failed and set an error */
1934 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1935 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1936 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1937 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1938 _("Error trying to delete a message. Trash folder not found"));
1941 g_object_unref (G_OBJECT (store_account));
1943 tny_folder_remove_msg (folder, header, &(priv->error));
1945 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1946 tny_folder_sync(folder, TRUE, &(priv->error));
1952 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1954 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1957 g_object_unref (G_OBJECT (folder));
1959 /* Notify about operation end */
1960 modest_mail_operation_notify_end (self, FALSE);
1964 transfer_msgs_status_cb (GObject *obj,
1968 XFerMsgAsyncHelper *helper = NULL;
1969 ModestMailOperation *self;
1970 ModestMailOperationPrivate *priv;
1971 ModestMailOperationState *state;
1974 g_return_if_fail (status != NULL);
1975 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1977 helper = (XFerMsgAsyncHelper *) user_data;
1978 g_return_if_fail (helper != NULL);
1980 self = helper->mail_op;
1981 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1983 if ((status->position == 1) && (status->of_total == 100))
1986 priv->done = status->position;
1987 priv->total = status->of_total;
1989 state = modest_mail_operation_clone_state (self);
1990 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1991 g_slice_free (ModestMailOperationState, state);
1996 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1998 XFerMsgAsyncHelper *helper;
1999 ModestMailOperation *self;
2000 ModestMailOperationPrivate *priv;
2002 helper = (XFerMsgAsyncHelper *) user_data;
2003 self = helper->mail_op;
2005 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2008 priv->error = g_error_copy (*err);
2010 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2011 } else if (cancelled) {
2012 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2013 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2015 _("Error trying to refresh the contents of %s"),
2016 tny_folder_get_name (folder));
2019 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2022 /* If user defined callback function was defined, call it */
2023 if (helper->user_callback) {
2024 gdk_threads_enter ();
2025 helper->user_callback (priv->source, helper->user_data);
2026 gdk_threads_leave ();
2030 g_object_unref (helper->headers);
2031 g_object_unref (helper->dest_folder);
2032 g_object_unref (helper->mail_op);
2033 g_slice_free (XFerMsgAsyncHelper, helper);
2034 g_object_unref (folder);
2036 /* Notify about operation end */
2037 modest_mail_operation_notify_end (self, TRUE);
2041 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2044 gboolean delete_original,
2045 XferMsgsAsynUserCallback user_callback,
2048 ModestMailOperationPrivate *priv;
2050 TnyFolder *src_folder;
2051 XFerMsgAsyncHelper *helper;
2053 ModestTnyFolderRules rules;
2055 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2056 g_return_if_fail (TNY_IS_LIST (headers));
2057 g_return_if_fail (TNY_IS_FOLDER (folder));
2059 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2062 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2064 /* Apply folder rules */
2065 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2066 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2067 /* Set status failed and set an error */
2068 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2069 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2070 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2071 _("FIXME: folder does not accept msgs"));
2072 /* Notify the queue */
2073 modest_mail_operation_notify_end (self, FALSE);
2077 /* Create the helper */
2078 helper = g_slice_new0 (XFerMsgAsyncHelper);
2079 helper->mail_op = g_object_ref(self);
2080 helper->dest_folder = g_object_ref(folder);
2081 helper->headers = g_object_ref(headers);
2082 helper->user_callback = user_callback;
2083 helper->user_data = user_data;
2085 /* Get source folder */
2086 iter = tny_list_create_iterator (headers);
2087 header = TNY_HEADER (tny_iterator_get_current (iter));
2088 src_folder = tny_header_get_folder (header);
2089 g_object_unref (header);
2090 g_object_unref (iter);
2092 /* Get account and set it into mail_operation */
2093 priv->account = modest_tny_folder_get_account (src_folder);
2095 /* Transfer messages */
2096 tny_folder_transfer_msgs_async (src_folder,
2101 transfer_msgs_status_cb,
2107 on_refresh_folder (TnyFolder *folder,
2112 RefreshAsyncHelper *helper = NULL;
2113 ModestMailOperation *self = NULL;
2114 ModestMailOperationPrivate *priv = NULL;
2116 helper = (RefreshAsyncHelper *) user_data;
2117 self = helper->mail_op;
2118 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2121 priv->error = g_error_copy (*error);
2122 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2127 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2128 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2129 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2130 _("Error trying to refresh the contents of %s"),
2131 tny_folder_get_name (folder));
2135 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2138 /* Call user defined callback, if it exists */
2139 if (helper->user_callback) {
2140 gdk_threads_enter ();
2141 helper->user_callback (priv->source, folder, helper->user_data);
2142 gdk_threads_leave ();
2146 g_object_unref (helper->mail_op);
2147 g_slice_free (RefreshAsyncHelper, helper);
2148 g_object_unref (folder);
2150 /* Notify about operation end */
2151 modest_mail_operation_notify_end (self, TRUE);
2155 on_refresh_folder_status_update (GObject *obj,
2159 RefreshAsyncHelper *helper = NULL;
2160 ModestMailOperation *self = NULL;
2161 ModestMailOperationPrivate *priv = NULL;
2162 ModestMailOperationState *state;
2164 g_return_if_fail (user_data != NULL);
2165 g_return_if_fail (status != NULL);
2166 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2168 helper = (RefreshAsyncHelper *) user_data;
2169 self = helper->mail_op;
2170 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2172 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2174 priv->done = status->position;
2175 priv->total = status->of_total;
2177 state = modest_mail_operation_clone_state (self);
2178 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2179 g_slice_free (ModestMailOperationState, state);
2183 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2185 RefreshAsyncUserCallback user_callback,
2188 ModestMailOperationPrivate *priv = NULL;
2189 RefreshAsyncHelper *helper = NULL;
2191 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2193 /* Pick a reference */
2194 g_object_ref (folder);
2196 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2198 /* Get account and set it into mail_operation */
2199 priv->account = modest_tny_folder_get_account (folder);
2201 /* Create the helper */
2202 helper = g_slice_new0 (RefreshAsyncHelper);
2203 helper->mail_op = g_object_ref(self);
2204 helper->user_callback = user_callback;
2205 helper->user_data = user_data;
2207 /* Refresh the folder. TODO: tinymail could issue a status
2208 updates before the callback call then this could happen. We
2209 must review the design */
2210 tny_folder_refresh_async (folder,
2212 on_refresh_folder_status_update,
2218 * It's used by the mail operation queue to notify the observers
2219 * attached to that signal that the operation finished. We need to use
2220 * that because tinymail does not give us the progress of a given
2221 * operation when it finishes (it directly calls the operation
2225 modest_mail_operation_notify_end (ModestMailOperation *self,
2228 ModestMailOperationState *state;
2229 ModestMailOperationPrivate *priv = NULL;
2231 g_return_if_fail (self);
2233 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2236 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2240 /* Set the account back to not busy */
2241 if (priv->account_name) {
2242 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2243 priv->account_name, FALSE);
2244 g_free(priv->account_name);
2245 priv->account_name = NULL;
2248 /* Notify the observers about the mail opertation end */
2249 state = modest_mail_operation_clone_state (self);
2251 gdk_threads_enter ();
2252 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2254 gdk_threads_leave ();
2255 g_slice_free (ModestMailOperationState, state);