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 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
803 ModestMailOperationState *state;
805 state = modest_mail_operation_clone_state (mail_op);
806 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
807 g_slice_free (ModestMailOperationState, state);
813 * Issues the "progress-changed" signal and removes the timer. It uses
814 * a lock to ensure that the progress information of the mail
815 * operation is not modified while there are notifications pending
818 idle_notify_progress_once (gpointer data)
822 pair = (ModestPair *) data;
824 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
826 /* Free the state and the reference to the mail operation */
827 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
828 g_object_unref (pair->first);
834 * Used by update_account_thread to notify the queue from the main
835 * loop. We call it inside an idle call to achieve that
838 idle_notify_update_account_queue (gpointer data)
840 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
841 ModestMailOperationPrivate *priv = NULL;
843 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
845 /* Do not need to block, the notify end will do it for us */
846 modest_mail_operation_notify_end (mail_op, TRUE);
847 g_object_unref (mail_op);
853 compare_headers_by_date (gconstpointer a,
856 TnyHeader **header1, **header2;
859 header1 = (TnyHeader **) a;
860 header2 = (TnyHeader **) b;
862 sent1 = tny_header_get_date_sent (*header1);
863 sent2 = tny_header_get_date_sent (*header2);
865 /* We want the most recent ones (greater time_t) at the
874 set_last_updated_idle (gpointer data)
876 gdk_threads_enter ();
878 /* It does not matter if the time is not exactly the same than
879 the time when this idle was called, it's just an
880 approximation and it won't be very different */
881 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
883 MODEST_ACCOUNT_LAST_UPDATED,
887 gdk_threads_leave ();
893 update_account_thread (gpointer thr_user_data)
895 static gboolean first_time = TRUE;
896 UpdateAccountInfo *info;
897 TnyList *all_folders = NULL;
898 GPtrArray *new_headers = NULL;
899 TnyIterator *iter = NULL;
900 TnyFolderStoreQuery *query = NULL;
901 ModestMailOperationPrivate *priv = NULL;
902 ModestTnySendQueue *send_queue = NULL;
904 info = (UpdateAccountInfo *) thr_user_data;
905 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
907 /* Get account and set it into mail_operation */
908 priv->account = g_object_ref (info->account);
911 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
912 * show any updates unless we do that
914 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
915 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
917 /* Get all the folders. We can do it synchronously because
918 we're already running in a different thread than the UI */
919 all_folders = tny_simple_list_new ();
920 query = tny_folder_store_query_new ();
921 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
922 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
927 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
931 iter = tny_list_create_iterator (all_folders);
932 while (!tny_iterator_is_done (iter)) {
933 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
935 recurse_folders (folder, query, all_folders);
936 tny_iterator_next (iter);
938 g_object_unref (G_OBJECT (iter));
940 /* Update status and notify. We need to call the notification
941 with a source function in order to call it from the main
942 loop. We need that in order not to get into trouble with
943 Gtk+. We use a timeout in order to provide more status
944 information, because the sync tinymail call does not
945 provide it for the moment */
946 gint timeout = g_timeout_add (100, idle_notify_progress, info->mail_op);
948 /* Refresh folders */
949 new_headers = g_ptr_array_new ();
950 iter = tny_list_create_iterator (all_folders);
952 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
954 InternalFolderObserver *observer;
955 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
957 /* Refresh the folder */
958 /* Our observer receives notification of new emails during folder refreshes,
959 * so we can use observer->new_headers.
961 observer = g_object_new (internal_folder_observer_get_type (), NULL);
962 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
964 /* This gets the status information (headers) from the server.
965 * We use the blocking version, because we are already in a separate
969 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
970 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
973 /* If the retrieve type is full messages, refresh and get the messages */
974 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
976 iter = tny_list_create_iterator (observer->new_headers);
977 while (!tny_iterator_is_done (iter)) {
978 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
980 /* Apply per-message size limits */
981 if (tny_header_get_message_size (header) < info->max_size)
982 g_ptr_array_add (new_headers, g_object_ref (header));
984 g_object_unref (header);
985 tny_iterator_next (iter);
987 g_object_unref (iter);
989 /* We do not need to do it the first time
990 because it's automatically done by the tree
992 if (G_UNLIKELY (!first_time))
993 tny_folder_poke_status (TNY_FOLDER (folder));
995 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
996 g_object_unref (observer);
999 g_object_unref (G_OBJECT (folder));
1002 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1006 tny_iterator_next (iter);
1009 did_a_cancel = FALSE;
1011 g_object_unref (G_OBJECT (iter));
1012 g_source_remove (timeout);
1014 if (new_headers->len > 0) {
1018 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1020 /* Apply message count limit */
1021 /* If the number of messages exceeds the maximum, ask the
1022 * user to download them all,
1023 * as per the UI spec "Retrieval Limits" section in 4.4:
1025 if (new_headers->len > info->retrieve_limit) {
1026 /* TODO: Ask the user, instead of just
1028 * mail_nc_msg_count_limit_exceeded, with 'Get
1029 * all' and 'Newest only' buttons. */
1030 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1031 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1032 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1033 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1034 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1039 priv->total = MIN (new_headers->len, info->retrieve_limit);
1040 while (msg_num < priv->total) {
1042 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1043 TnyFolder *folder = tny_header_get_folder (header);
1044 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1045 ModestMailOperationState *state;
1049 /* We can not just use the mail operation because the
1050 values of done and total could change before the
1052 state = modest_mail_operation_clone_state (info->mail_op);
1053 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1054 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1055 pair, (GDestroyNotify) modest_pair_free);
1057 g_object_unref (msg);
1058 g_object_unref (folder);
1062 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1063 g_ptr_array_free (new_headers, FALSE);
1067 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1070 if (priv->account != NULL)
1071 g_object_unref (priv->account);
1072 priv->account = g_object_ref (info->transport_account);
1074 send_queue = modest_runtime_get_send_queue (info->transport_account);
1076 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1077 modest_tny_send_queue_try_to_send (send_queue);
1078 g_source_remove (timeout);
1080 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1081 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1082 "cannot create a send queue for %s\n",
1083 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1084 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1087 /* Check if the operation was a success */
1089 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1091 /* Update the last updated key */
1092 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1093 set_last_updated_idle,
1094 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1095 (GDestroyNotify) g_free);
1099 /* Notify about operation end. Note that the info could be
1100 freed before this idle happens, but the mail operation will
1102 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1104 if (info->callback) {
1105 /* This thread is not in the main lock */
1106 gdk_threads_enter ();
1107 info->callback (info->mail_op,
1108 (new_headers) ? new_headers->len : 0,
1110 gdk_threads_leave ();
1114 g_object_unref (query);
1115 g_object_unref (all_folders);
1116 g_object_unref (info->account);
1117 g_object_unref (info->transport_account);
1118 g_free (info->retrieve_type);
1119 g_slice_free (UpdateAccountInfo, info);
1127 modest_mail_operation_update_account (ModestMailOperation *self,
1128 const gchar *account_name,
1129 UpdateAccountCallback callback,
1133 UpdateAccountInfo *info;
1134 ModestMailOperationPrivate *priv;
1135 ModestAccountMgr *mgr;
1136 TnyStoreAccount *modest_account;
1137 TnyTransportAccount *transport_account;
1139 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1140 g_return_val_if_fail (account_name, FALSE);
1142 /* Init mail operation. Set total and done to 0, and do not
1143 update them, this way the progress objects will know that
1144 we have no clue about the number of the objects */
1145 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1148 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1150 /* Make sure that we have a connection, and request one
1152 * TODO: Is there some way to trigger this for every attempt to
1153 * use the network? */
1154 if (!modest_platform_connect_and_wait(NULL))
1157 /* Get the Modest account */
1158 modest_account = (TnyStoreAccount *)
1159 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1161 TNY_ACCOUNT_TYPE_STORE);
1163 if (!modest_account) {
1164 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1165 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1166 "cannot get tny store account for %s\n", account_name);
1171 /* Get the transport account, we can not do it in the thread
1172 due to some problems with dbus */
1173 transport_account = (TnyTransportAccount *)
1174 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1176 if (!transport_account) {
1177 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1178 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1179 "cannot get tny transport account for %s\n", account_name);
1183 /* Create the helper object */
1184 info = g_slice_new (UpdateAccountInfo);
1185 info->mail_op = self;
1186 info->account = modest_account;
1187 info->transport_account = transport_account;
1188 info->callback = callback;
1189 info->user_data = user_data;
1191 /* Get the message size limit */
1192 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1193 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1194 if (info->max_size == 0)
1195 info->max_size = G_MAXINT;
1197 info->max_size = info->max_size * KB;
1199 /* Get per-account retrieval type */
1200 mgr = modest_runtime_get_account_mgr ();
1201 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1202 MODEST_ACCOUNT_RETRIEVE, FALSE);
1204 /* Get per-account message amount retrieval limit */
1205 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1206 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1207 if (info->retrieve_limit == 0)
1208 info->retrieve_limit = G_MAXINT;
1210 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1212 /* Set account busy */
1213 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1214 priv->account_name = g_strdup(account_name);
1216 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1221 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1223 callback (self, 0, user_data);
1224 modest_mail_operation_notify_end (self, FALSE);
1228 /* ******************************************************************* */
1229 /* ************************** STORE ACTIONS ************************* */
1230 /* ******************************************************************* */
1234 modest_mail_operation_create_folder (ModestMailOperation *self,
1235 TnyFolderStore *parent,
1238 ModestMailOperationPrivate *priv;
1239 TnyFolder *new_folder = NULL;
1241 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1242 g_return_val_if_fail (name, NULL);
1244 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1247 if (TNY_IS_FOLDER (parent)) {
1248 /* Check folder rules */
1249 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1250 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1251 /* Set status failed and set an error */
1252 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1253 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1254 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1255 _("mail_in_ui_folder_create_error"));
1259 if (!strcmp (name, " ") || strchr (name, '/')) {
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 /* Create the folder */
1268 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1269 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1271 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1274 /* Notify about operation end */
1275 modest_mail_operation_notify_end (self, FALSE);
1281 modest_mail_operation_remove_folder (ModestMailOperation *self,
1283 gboolean remove_to_trash)
1285 TnyAccount *account;
1286 ModestMailOperationPrivate *priv;
1287 ModestTnyFolderRules rules;
1289 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1290 g_return_if_fail (TNY_IS_FOLDER (folder));
1292 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1294 /* Check folder rules */
1295 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1296 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1297 /* Set status failed and set an error */
1298 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1299 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1300 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1301 _("mail_in_ui_folder_delete_error"));
1305 /* Get the account */
1306 account = modest_tny_folder_get_account (folder);
1307 priv->account = g_object_ref(account);
1309 /* Delete folder or move to trash */
1310 if (remove_to_trash) {
1311 TnyFolder *trash_folder = NULL;
1312 trash_folder = modest_tny_account_get_special_folder (account,
1313 TNY_FOLDER_TYPE_TRASH);
1314 /* TODO: error_handling */
1315 modest_mail_operation_xfer_folder (self, folder,
1316 TNY_FOLDER_STORE (trash_folder), TRUE);
1318 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1320 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1321 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1324 g_object_unref (G_OBJECT (parent));
1326 g_object_unref (G_OBJECT (account));
1329 /* Notify about operation end */
1330 modest_mail_operation_notify_end (self, FALSE);
1334 transfer_folder_status_cb (GObject *obj,
1338 ModestMailOperation *self;
1339 ModestMailOperationPrivate *priv;
1340 ModestMailOperationState *state;
1342 g_return_if_fail (status != NULL);
1343 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1345 self = MODEST_MAIL_OPERATION (user_data);
1346 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1348 if ((status->position == 1) && (status->of_total == 100))
1351 priv->done = status->position;
1352 priv->total = status->of_total;
1354 state = modest_mail_operation_clone_state (self);
1355 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1356 g_slice_free (ModestMailOperationState, state);
1361 transfer_folder_cb (TnyFolder *folder,
1362 TnyFolderStore *into,
1364 TnyFolder *new_folder,
1368 ModestMailOperation *self = NULL;
1369 ModestMailOperationPrivate *priv = NULL;
1371 self = MODEST_MAIL_OPERATION (user_data);
1373 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1376 priv->error = g_error_copy (*err);
1378 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1379 } else if (cancelled) {
1380 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1381 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1382 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1383 _("Transference of %s was cancelled."),
1384 tny_folder_get_name (folder));
1387 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1391 g_object_unref (folder);
1392 g_object_unref (into);
1394 /* Notify about operation end */
1395 modest_mail_operation_notify_end (self, TRUE);
1399 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1401 TnyFolderStore *parent,
1402 gboolean delete_original)
1404 ModestMailOperationPrivate *priv = NULL;
1405 ModestTnyFolderRules parent_rules = 0, rules;
1407 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1408 g_return_if_fail (TNY_IS_FOLDER (folder));
1410 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1412 /* Get account and set it into mail_operation */
1413 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1414 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1416 /* Get folder rules */
1417 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1418 if (TNY_IS_FOLDER (parent))
1419 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1421 /* The moveable restriction is applied also to copy operation */
1422 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1423 printf("DEBUG: %s: Not allowing the move.\n", __FUNCTION__);
1424 /* Set status failed and set an error */
1425 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1426 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1427 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1428 _("mail_in_ui_folder_move_target_error"));
1430 /* Notify the queue */
1431 modest_mail_operation_notify_end (self, FALSE);
1432 } else if (TNY_IS_FOLDER (parent) &&
1433 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1434 /* Set status failed and set an error */
1435 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1436 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1437 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1438 _("FIXME: parent folder does not accept new folders"));
1440 /* Notify the queue */
1441 modest_mail_operation_notify_end (self, FALSE);
1443 /* Pick references for async calls */
1444 g_object_ref (folder);
1445 g_object_ref (parent);
1447 /* Move/Copy folder */
1448 tny_folder_copy_async (folder,
1450 tny_folder_get_name (folder),
1453 transfer_folder_status_cb,
1459 modest_mail_operation_rename_folder (ModestMailOperation *self,
1463 ModestMailOperationPrivate *priv;
1464 ModestTnyFolderRules rules;
1466 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1467 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1468 g_return_if_fail (name);
1470 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1472 /* Get account and set it into mail_operation */
1473 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1475 /* Check folder rules */
1476 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1477 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1478 /* Set status failed and set an error */
1479 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1480 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1481 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1482 _("FIXME: unable to rename"));
1484 /* Notify about operation end */
1485 modest_mail_operation_notify_end (self, FALSE);
1486 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1487 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1488 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1489 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1490 _("FIXME: unable to rename"));
1491 /* Notify about operation end */
1492 modest_mail_operation_notify_end (self, FALSE);
1494 TnyFolderStore *into;
1496 /* Rename. Camel handles folder subscription/unsubscription */
1497 into = tny_folder_get_folder_store (folder);
1498 tny_folder_copy_async (folder, into, name, TRUE,
1500 transfer_folder_status_cb,
1503 g_object_unref (into);
1507 /* ******************************************************************* */
1508 /* ************************** MSG ACTIONS ************************* */
1509 /* ******************************************************************* */
1511 void modest_mail_operation_get_msg (ModestMailOperation *self,
1513 GetMsgAsyncUserCallback user_callback,
1516 GetMsgAsyncHelper *helper = NULL;
1518 ModestMailOperationPrivate *priv;
1520 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1521 g_return_if_fail (TNY_IS_HEADER (header));
1523 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1524 folder = tny_header_get_folder (header);
1526 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1528 /* Get message from folder */
1530 /* Get account and set it into mail_operation */
1531 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1533 helper = g_slice_new0 (GetMsgAsyncHelper);
1534 helper->mail_op = self;
1535 helper->user_callback = user_callback;
1536 helper->user_data = user_data;
1537 helper->header = g_object_ref (header);
1539 // The callback's reference so that the mail op is not
1540 // finalized until the async operation is completed even if
1541 // the user canceled the request meanwhile.
1542 g_object_ref (G_OBJECT (helper->mail_op));
1544 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1546 g_object_unref (G_OBJECT (folder));
1548 /* Set status failed and set an error */
1549 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1550 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1551 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1552 _("Error trying to get a message. No folder found for header"));
1554 /* Notify the queue */
1555 modest_mail_operation_notify_end (self, FALSE);
1560 get_msg_cb (TnyFolder *folder,
1566 GetMsgAsyncHelper *helper = NULL;
1567 ModestMailOperation *self = NULL;
1568 ModestMailOperationPrivate *priv = NULL;
1570 helper = (GetMsgAsyncHelper *) user_data;
1571 g_return_if_fail (helper != NULL);
1572 self = helper->mail_op;
1573 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1574 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1576 /* Check errors and cancel */
1578 priv->error = g_error_copy (*error);
1579 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1583 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1584 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1585 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1586 _("Error trying to refresh the contents of %s"),
1587 tny_folder_get_name (folder));
1591 /* The mail operation might have been canceled in which case we do not
1592 want to notify anyone anymore. */
1593 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED) {
1594 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1596 /* If user defined callback function was defined, call it */
1597 if (helper->user_callback) {
1598 /* This callback is called into an iddle by tinymail,
1599 and idles are not in the main lock */
1600 gdk_threads_enter ();
1601 helper->user_callback (self, helper->header, msg, helper->user_data);
1602 gdk_threads_leave ();
1608 g_object_unref (helper->header);
1609 g_slice_free (GetMsgAsyncHelper, helper);
1611 /* Notify about operation end */
1612 if(priv->status != MODEST_MAIL_OPERATION_STATUS_CANCELED)
1613 modest_mail_operation_notify_end (self, TRUE);
1615 g_object_unref (G_OBJECT (self));
1619 get_msg_status_cb (GObject *obj,
1623 GetMsgAsyncHelper *helper = NULL;
1624 ModestMailOperation *self;
1625 ModestMailOperationPrivate *priv;
1626 ModestMailOperationState *state;
1628 g_return_if_fail (status != NULL);
1629 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1631 helper = (GetMsgAsyncHelper *) user_data;
1632 g_return_if_fail (helper != NULL);
1634 self = helper->mail_op;
1635 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1637 if(priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
1640 if ((status->position == 1) && (status->of_total == 100))
1646 state = modest_mail_operation_clone_state (self);
1647 state->bytes_done = status->position;
1648 state->bytes_total = status->of_total;
1649 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1650 g_slice_free (ModestMailOperationState, state);
1653 /****************************************************/
1655 ModestMailOperation *mail_op;
1657 GetMsgAsyncUserCallback user_callback;
1659 GDestroyNotify notify;
1663 GetMsgAsyncUserCallback user_callback;
1667 ModestMailOperation *mail_op;
1668 } NotifyGetMsgsInfo;
1672 * Used by get_msgs_full_thread to call the user_callback for each
1673 * message that has been read
1676 notify_get_msgs_full (gpointer data)
1678 NotifyGetMsgsInfo *info;
1680 info = (NotifyGetMsgsInfo *) data;
1682 /* Call the user callback. Idles are not in the main lock, so
1684 gdk_threads_enter ();
1685 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1686 gdk_threads_leave ();
1688 g_slice_free (NotifyGetMsgsInfo, info);
1694 * Used by get_msgs_full_thread to free al the thread resources and to
1695 * call the destroy function for the passed user_data
1698 get_msgs_full_destroyer (gpointer data)
1700 GetFullMsgsInfo *info;
1702 info = (GetFullMsgsInfo *) data;
1705 gdk_threads_enter ();
1706 info->notify (info->user_data);
1707 gdk_threads_leave ();
1711 g_object_unref (info->headers);
1712 g_slice_free (GetFullMsgsInfo, info);
1718 get_msgs_full_thread (gpointer thr_user_data)
1720 GetFullMsgsInfo *info;
1721 ModestMailOperationPrivate *priv = NULL;
1722 TnyIterator *iter = NULL;
1724 info = (GetFullMsgsInfo *) thr_user_data;
1725 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1727 iter = tny_list_create_iterator (info->headers);
1728 while (!tny_iterator_is_done (iter)) {
1732 header = TNY_HEADER (tny_iterator_get_current (iter));
1733 folder = tny_header_get_folder (header);
1735 /* Get message from folder */
1738 /* The callback will call it per each header */
1739 msg = tny_folder_get_msg (folder, header, &(priv->error));
1742 ModestMailOperationState *state;
1747 /* notify progress */
1748 state = modest_mail_operation_clone_state (info->mail_op);
1749 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1750 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1751 pair, (GDestroyNotify) modest_pair_free);
1753 /* The callback is the responsible for
1754 freeing the message */
1755 if (info->user_callback) {
1756 NotifyGetMsgsInfo *info_notify;
1757 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1758 info_notify->user_callback = info->user_callback;
1759 info_notify->mail_op = info->mail_op;
1760 info_notify->header = g_object_ref (header);
1761 info_notify->msg = g_object_ref (msg);
1762 info_notify->user_data = info->user_data;
1763 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1764 notify_get_msgs_full,
1767 g_object_unref (msg);
1770 /* Set status failed and set an error */
1771 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1772 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1773 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1774 "Error trying to get a message. No folder found for header");
1776 g_object_unref (header);
1777 tny_iterator_next (iter);
1780 /* Set operation status */
1781 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1782 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1784 /* Notify about operation end */
1785 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1787 /* Free thread resources. Will be called after all previous idles */
1788 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1794 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1795 TnyList *header_list,
1796 GetMsgAsyncUserCallback user_callback,
1798 GDestroyNotify notify)
1800 TnyHeader *header = NULL;
1801 TnyFolder *folder = NULL;
1803 ModestMailOperationPrivate *priv = NULL;
1804 GetFullMsgsInfo *info = NULL;
1805 gboolean size_ok = TRUE;
1807 TnyIterator *iter = NULL;
1809 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1811 /* Init mail operation */
1812 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1813 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1815 priv->total = tny_list_get_length(header_list);
1817 /* Get account and set it into mail_operation */
1818 if (tny_list_get_length (header_list) >= 1) {
1819 iter = tny_list_create_iterator (header_list);
1820 header = TNY_HEADER (tny_iterator_get_current (iter));
1821 folder = tny_header_get_folder (header);
1822 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1823 g_object_unref (header);
1824 g_object_unref (folder);
1826 if (tny_list_get_length (header_list) == 1) {
1827 g_object_unref (iter);
1832 /* Get msg size limit */
1833 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1834 MODEST_CONF_MSG_SIZE_LIMIT,
1837 g_clear_error (&(priv->error));
1838 max_size = G_MAXINT;
1840 max_size = max_size * KB;
1843 /* Check message size limits. If there is only one message
1844 always retrieve it */
1846 while (!tny_iterator_is_done (iter) && size_ok) {
1847 header = TNY_HEADER (tny_iterator_get_current (iter));
1848 if (tny_header_get_message_size (header) >= max_size)
1850 g_object_unref (header);
1851 tny_iterator_next (iter);
1853 g_object_unref (iter);
1857 /* Create the info */
1858 info = g_slice_new0 (GetFullMsgsInfo);
1859 info->mail_op = self;
1860 info->user_callback = user_callback;
1861 info->user_data = user_data;
1862 info->headers = g_object_ref (header_list);
1863 info->notify = notify;
1865 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1867 /* Set status failed and set an error */
1868 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1869 /* FIXME: the error msg is different for pop */
1870 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1871 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1872 _("emev_ni_ui_imap_msg_size_exceed_error"));
1873 /* Remove from queue and free resources */
1874 modest_mail_operation_notify_end (self, FALSE);
1882 modest_mail_operation_remove_msg (ModestMailOperation *self,
1884 gboolean remove_to_trash)
1887 ModestMailOperationPrivate *priv;
1889 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1890 g_return_if_fail (TNY_IS_HEADER (header));
1892 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1893 folder = tny_header_get_folder (header);
1895 /* Get account and set it into mail_operation */
1896 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1898 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1900 /* Delete or move to trash */
1901 if (remove_to_trash) {
1902 TnyFolder *trash_folder;
1903 TnyStoreAccount *store_account;
1905 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1906 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1907 TNY_FOLDER_TYPE_TRASH);
1912 headers = tny_simple_list_new ();
1913 tny_list_append (headers, G_OBJECT (header));
1914 g_object_unref (header);
1917 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1918 g_object_unref (headers);
1919 /* g_object_unref (trash_folder); */
1921 ModestMailOperationPrivate *priv;
1923 /* Set status failed and set an error */
1924 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1925 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1926 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1927 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1928 _("Error trying to delete a message. Trash folder not found"));
1931 g_object_unref (G_OBJECT (store_account));
1933 tny_folder_remove_msg (folder, header, &(priv->error));
1935 tny_header_set_flags (header, TNY_HEADER_FLAG_DELETED);
1936 tny_folder_sync(folder, TRUE, &(priv->error));
1942 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1944 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1947 g_object_unref (G_OBJECT (folder));
1949 /* Notify about operation end */
1950 modest_mail_operation_notify_end (self, FALSE);
1954 transfer_msgs_status_cb (GObject *obj,
1958 XFerMsgAsyncHelper *helper = NULL;
1959 ModestMailOperation *self;
1960 ModestMailOperationPrivate *priv;
1961 ModestMailOperationState *state;
1964 g_return_if_fail (status != NULL);
1965 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1967 helper = (XFerMsgAsyncHelper *) user_data;
1968 g_return_if_fail (helper != NULL);
1970 self = helper->mail_op;
1971 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1973 if ((status->position == 1) && (status->of_total == 100))
1976 priv->done = status->position;
1977 priv->total = status->of_total;
1979 state = modest_mail_operation_clone_state (self);
1980 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1981 g_slice_free (ModestMailOperationState, state);
1986 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1988 XFerMsgAsyncHelper *helper;
1989 ModestMailOperation *self;
1990 ModestMailOperationPrivate *priv;
1992 helper = (XFerMsgAsyncHelper *) user_data;
1993 self = helper->mail_op;
1995 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1998 priv->error = g_error_copy (*err);
2000 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2001 } else if (cancelled) {
2002 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2003 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2004 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2005 _("Error trying to refresh the contents of %s"),
2006 tny_folder_get_name (folder));
2009 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2012 /* Notify about operation end */
2013 modest_mail_operation_notify_end (self, TRUE);
2015 /* If user defined callback function was defined, call it */
2016 if (helper->user_callback) {
2017 gdk_threads_enter ();
2018 helper->user_callback (priv->source, helper->user_data);
2019 gdk_threads_leave ();
2023 g_object_unref (helper->headers);
2024 g_object_unref (helper->dest_folder);
2025 g_object_unref (helper->mail_op);
2026 g_slice_free (XFerMsgAsyncHelper, helper);
2027 g_object_unref (folder);
2032 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2035 gboolean delete_original,
2036 XferMsgsAsynUserCallback user_callback,
2039 ModestMailOperationPrivate *priv;
2041 TnyFolder *src_folder;
2042 XFerMsgAsyncHelper *helper;
2044 ModestTnyFolderRules rules;
2046 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2047 g_return_if_fail (TNY_IS_LIST (headers));
2048 g_return_if_fail (TNY_IS_FOLDER (folder));
2050 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2053 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2055 /* Apply folder rules */
2056 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2057 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2058 /* Set status failed and set an error */
2059 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2060 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2061 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2062 _("FIXME: folder does not accept msgs"));
2063 /* Notify the queue */
2064 modest_mail_operation_notify_end (self, FALSE);
2068 /* Create the helper */
2069 helper = g_slice_new0 (XFerMsgAsyncHelper);
2070 helper->mail_op = g_object_ref(self);
2071 helper->dest_folder = g_object_ref(folder);
2072 helper->headers = g_object_ref(headers);
2073 helper->user_callback = user_callback;
2074 helper->user_data = user_data;
2076 /* Get source folder */
2077 iter = tny_list_create_iterator (headers);
2078 header = TNY_HEADER (tny_iterator_get_current (iter));
2079 src_folder = tny_header_get_folder (header);
2080 g_object_unref (header);
2081 g_object_unref (iter);
2083 /* Get account and set it into mail_operation */
2084 priv->account = modest_tny_folder_get_account (src_folder);
2086 /* Transfer messages */
2087 tny_folder_transfer_msgs_async (src_folder,
2092 transfer_msgs_status_cb,
2098 on_refresh_folder (TnyFolder *folder,
2103 RefreshAsyncHelper *helper = NULL;
2104 ModestMailOperation *self = NULL;
2105 ModestMailOperationPrivate *priv = NULL;
2107 helper = (RefreshAsyncHelper *) user_data;
2108 self = helper->mail_op;
2109 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2112 priv->error = g_error_copy (*error);
2113 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2118 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2119 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2120 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2121 _("Error trying to refresh the contents of %s"),
2122 tny_folder_get_name (folder));
2126 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2129 /* Call user defined callback, if it exists */
2130 if (helper->user_callback) {
2131 gdk_threads_enter ();
2132 helper->user_callback (priv->source, folder, helper->user_data);
2133 gdk_threads_leave ();
2137 g_object_unref (helper->mail_op);
2138 g_slice_free (RefreshAsyncHelper, helper);
2139 g_object_unref (folder);
2141 /* Notify about operation end */
2142 modest_mail_operation_notify_end (self, TRUE);
2146 on_refresh_folder_status_update (GObject *obj,
2150 RefreshAsyncHelper *helper = NULL;
2151 ModestMailOperation *self = NULL;
2152 ModestMailOperationPrivate *priv = NULL;
2153 ModestMailOperationState *state;
2155 g_return_if_fail (user_data != NULL);
2156 g_return_if_fail (status != NULL);
2157 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2159 helper = (RefreshAsyncHelper *) user_data;
2160 self = helper->mail_op;
2161 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2163 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2165 priv->done = status->position;
2166 priv->total = status->of_total;
2168 state = modest_mail_operation_clone_state (self);
2169 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2170 g_slice_free (ModestMailOperationState, state);
2174 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2176 RefreshAsyncUserCallback user_callback,
2179 ModestMailOperationPrivate *priv = NULL;
2180 RefreshAsyncHelper *helper = NULL;
2182 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2184 /* Pick a reference */
2185 g_object_ref (folder);
2187 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2189 /* Get account and set it into mail_operation */
2190 priv->account = modest_tny_folder_get_account (folder);
2192 /* Create the helper */
2193 helper = g_slice_new0 (RefreshAsyncHelper);
2194 helper->mail_op = g_object_ref(self);
2195 helper->user_callback = user_callback;
2196 helper->user_data = user_data;
2198 /* Refresh the folder. TODO: tinymail could issue a status
2199 updates before the callback call then this could happen. We
2200 must review the design */
2201 tny_folder_refresh_async (folder,
2203 on_refresh_folder_status_update,
2209 * It's used by the mail operation queue to notify the observers
2210 * attached to that signal that the operation finished. We need to use
2211 * that because tinymail does not give us the progress of a given
2212 * operation when it finishes (it directly calls the operation
2216 modest_mail_operation_notify_end (ModestMailOperation *self,
2219 ModestMailOperationState *state;
2220 ModestMailOperationPrivate *priv = NULL;
2222 g_return_if_fail (self);
2224 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2227 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2231 /* Set the account back to not busy */
2232 if (priv->account_name) {
2233 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2234 priv->account_name, FALSE);
2235 g_free(priv->account_name);
2236 priv->account_name = NULL;
2239 /* Notify the observers about the mail opertation end */
2240 state = modest_mail_operation_clone_state (self);
2242 gdk_threads_enter ();
2243 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2245 gdk_threads_leave ();
2246 g_slice_free (ModestMailOperationState, state);