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);
75 static gboolean did_a_cancel = FALSE;
77 enum _ModestMailOperationSignals
79 PROGRESS_CHANGED_SIGNAL,
84 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
85 struct _ModestMailOperationPrivate {
92 ErrorCheckingUserCallback error_checking;
93 gpointer error_checking_user_data;
94 ModestMailOperationStatus status;
95 ModestMailOperationTypeOperation op_type;
98 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
99 MODEST_TYPE_MAIL_OPERATION, \
100 ModestMailOperationPrivate))
102 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
103 priv->status = new_status;\
106 typedef struct _GetMsgAsyncHelper {
107 ModestMailOperation *mail_op;
109 GetMsgAsyncUserCallback user_callback;
113 typedef struct _RefreshAsyncHelper {
114 ModestMailOperation *mail_op;
115 RefreshAsyncUserCallback user_callback;
117 } RefreshAsyncHelper;
119 typedef struct _XFerMsgAsyncHelper
121 ModestMailOperation *mail_op;
123 TnyFolder *dest_folder;
124 XferMsgsAsynUserCallback user_callback;
126 } XFerMsgAsyncHelper;
129 static GObjectClass *parent_class = NULL;
131 static guint signals[NUM_SIGNALS] = {0};
134 modest_mail_operation_get_type (void)
136 static GType my_type = 0;
138 static const GTypeInfo my_info = {
139 sizeof(ModestMailOperationClass),
140 NULL, /* base init */
141 NULL, /* base finalize */
142 (GClassInitFunc) modest_mail_operation_class_init,
143 NULL, /* class finalize */
144 NULL, /* class data */
145 sizeof(ModestMailOperation),
147 (GInstanceInitFunc) modest_mail_operation_init,
150 my_type = g_type_register_static (G_TYPE_OBJECT,
151 "ModestMailOperation",
158 modest_mail_operation_class_init (ModestMailOperationClass *klass)
160 GObjectClass *gobject_class;
161 gobject_class = (GObjectClass*) klass;
163 parent_class = g_type_class_peek_parent (klass);
164 gobject_class->finalize = modest_mail_operation_finalize;
166 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
169 * ModestMailOperation::progress-changed
170 * @self: the #MailOperation that emits the signal
171 * @user_data: user data set when the signal handler was connected
173 * Emitted when the progress of a mail operation changes
175 signals[PROGRESS_CHANGED_SIGNAL] =
176 g_signal_new ("progress-changed",
177 G_TYPE_FROM_CLASS (gobject_class),
179 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
181 g_cclosure_marshal_VOID__POINTER,
182 G_TYPE_NONE, 1, G_TYPE_POINTER);
187 modest_mail_operation_init (ModestMailOperation *obj)
189 ModestMailOperationPrivate *priv;
191 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
193 priv->account = NULL;
194 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
195 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
200 priv->error_checking = NULL;
201 priv->error_checking_user_data = NULL;
205 modest_mail_operation_finalize (GObject *obj)
207 ModestMailOperationPrivate *priv;
209 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
214 g_error_free (priv->error);
218 g_object_unref (priv->source);
222 g_object_unref (priv->account);
223 priv->account = NULL;
227 G_OBJECT_CLASS(parent_class)->finalize (obj);
231 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
234 ModestMailOperation *obj;
235 ModestMailOperationPrivate *priv;
237 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
238 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
240 priv->op_type = op_type;
242 priv->source = g_object_ref(source);
248 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
250 ErrorCheckingUserCallback error_handler,
253 ModestMailOperation *obj;
254 ModestMailOperationPrivate *priv;
256 obj = modest_mail_operation_new (op_type, source);
257 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
259 g_return_val_if_fail (error_handler != NULL, obj);
260 priv->error_checking = error_handler;
266 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
268 ModestMailOperationPrivate *priv;
270 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
271 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
273 if (priv->error_checking != NULL)
274 priv->error_checking (self, priv->error_checking_user_data);
278 ModestMailOperationTypeOperation
279 modest_mail_operation_get_type_operation (ModestMailOperation *self)
281 ModestMailOperationPrivate *priv;
283 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
285 return priv->op_type;
289 modest_mail_operation_is_mine (ModestMailOperation *self,
292 ModestMailOperationPrivate *priv;
294 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
295 if (priv->source == NULL) return FALSE;
297 return priv->source == me;
301 modest_mail_operation_get_source (ModestMailOperation *self)
303 ModestMailOperationPrivate *priv;
305 g_return_val_if_fail (self, NULL);
307 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
309 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
313 return g_object_ref (priv->source);
316 ModestMailOperationStatus
317 modest_mail_operation_get_status (ModestMailOperation *self)
319 ModestMailOperationPrivate *priv;
321 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
322 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
323 MODEST_MAIL_OPERATION_STATUS_INVALID);
325 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
327 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
328 return MODEST_MAIL_OPERATION_STATUS_INVALID;
335 modest_mail_operation_get_error (ModestMailOperation *self)
337 ModestMailOperationPrivate *priv;
339 g_return_val_if_fail (self, NULL);
340 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
342 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
345 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
353 modest_mail_operation_cancel (ModestMailOperation *self)
355 ModestMailOperationPrivate *priv;
357 if (!MODEST_IS_MAIL_OPERATION (self)) {
358 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
362 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
364 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
368 /* Notify about operation end */
369 modest_mail_operation_notify_end (self);
374 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
376 modest_mail_operation_queue_cancel_all (modest_runtime_get_mail_operation_queue());
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);
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 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
624 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
625 "modest: failed to create a new msg\n");
629 /* add priority flags */
630 header = tny_msg_get_header (msg);
631 tny_header_set_flags (header, priority_flags);
633 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
635 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
636 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
637 "modest: failed to create a new msg\n");
641 if (draft_msg != NULL) {
642 header = tny_msg_get_header (draft_msg);
643 /* Remove the old draft expunging it */
644 tny_folder_remove_msg (folder, header, NULL);
645 tny_folder_sync (folder, TRUE, NULL);
646 g_object_unref (header);
649 tny_folder_add_msg (folder, msg, &(priv->error));
655 g_object_unref (G_OBJECT(msg));
657 g_object_unref (G_OBJECT(folder));
659 modest_mail_operation_notify_end (self);
664 ModestMailOperation *mail_op;
665 TnyStoreAccount *account;
666 TnyTransportAccount *transport_account;
669 gchar *retrieve_type;
673 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
674 /* We use this folder observer to track the headers that have been
675 * added to a folder */
678 TnyList *new_headers;
679 } InternalFolderObserver;
683 } InternalFolderObserverClass;
685 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
687 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
688 internal_folder_observer,
690 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
694 foreach_add_item (gpointer header, gpointer user_data)
696 /* printf("DEBUG: %s: header subject=%s\n",
697 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
699 tny_list_prepend (TNY_LIST (user_data),
700 g_object_ref (G_OBJECT (header)));
703 /* This is the method that looks for new messages in a folder */
705 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
707 InternalFolderObserver *derived = (InternalFolderObserver *)self;
709 TnyFolderChangeChanged changed;
711 changed = tny_folder_change_get_changed (change);
713 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
716 /* Get added headers */
717 list = tny_simple_list_new ();
718 tny_folder_change_get_added_headers (change, list);
720 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
721 * __FUNCTION__, tny_list_get_length(list));
724 /* Add them to the folder observer */
725 tny_list_foreach (list, foreach_add_item,
726 derived->new_headers);
728 g_object_unref (G_OBJECT (list));
733 internal_folder_observer_init (InternalFolderObserver *self)
735 self->new_headers = tny_simple_list_new ();
738 internal_folder_observer_finalize (GObject *object)
740 InternalFolderObserver *self;
742 self = (InternalFolderObserver *) object;
743 g_object_unref (self->new_headers);
745 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
748 tny_folder_observer_init (TnyFolderObserverIface *iface)
750 iface->update_func = internal_folder_observer_update;
753 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
755 GObjectClass *object_class;
757 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
758 object_class = (GObjectClass*) klass;
759 object_class->finalize = internal_folder_observer_finalize;
765 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
768 TnyList *folders = tny_simple_list_new ();
770 tny_folder_store_get_folders (store, folders, query, NULL);
771 iter = tny_list_create_iterator (folders);
773 while (!tny_iterator_is_done (iter)) {
775 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
777 tny_list_prepend (all_folders, G_OBJECT (folder));
778 recurse_folders (folder, query, all_folders);
779 g_object_unref (G_OBJECT (folder));
781 tny_iterator_next (iter);
783 g_object_unref (G_OBJECT (iter));
784 g_object_unref (G_OBJECT (folders));
788 * Issues the "progress-changed" signal. The timer won't be removed,
789 * so you must call g_source_remove to stop the signal emission
792 idle_notify_progress (gpointer data)
794 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
795 ModestMailOperationState *state;
797 state = modest_mail_operation_clone_state (mail_op);
798 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
799 g_slice_free (ModestMailOperationState, state);
805 * Issues the "progress-changed" signal and removes the timer. It uses
806 * a lock to ensure that the progress information of the mail
807 * operation is not modified while there are notifications pending
810 idle_notify_progress_once (gpointer data)
814 pair = (ModestPair *) data;
816 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
818 /* Free the state and the reference to the mail operation */
819 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
820 g_object_unref (pair->first);
826 * Used by update_account_thread to notify the queue from the main
827 * loop. We call it inside an idle call to achieve that
830 notify_update_account_queue (gpointer data)
832 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
833 ModestMailOperationPrivate *priv = NULL;
835 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
837 modest_mail_operation_notify_end (mail_op);
838 g_object_unref (mail_op);
844 compare_headers_by_date (gconstpointer a,
847 TnyHeader **header1, **header2;
850 header1 = (TnyHeader **) a;
851 header2 = (TnyHeader **) b;
853 sent1 = tny_header_get_date_sent (*header1);
854 sent2 = tny_header_get_date_sent (*header2);
856 /* We want the most recent ones (greater time_t) at the
865 set_last_updated_idle (gpointer data)
867 /* It does not matter if the time is not exactly the same than
868 the time when this idle was called, it's just an
869 approximation and it won't be very different */
870 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
872 MODEST_ACCOUNT_LAST_UPDATED,
880 update_account_thread (gpointer thr_user_data)
882 static gboolean first_time = TRUE;
883 UpdateAccountInfo *info;
884 TnyList *all_folders = NULL;
885 GPtrArray *new_headers;
886 TnyIterator *iter = NULL;
887 TnyFolderStoreQuery *query = NULL;
888 ModestMailOperationPrivate *priv;
889 ModestTnySendQueue *send_queue;
891 info = (UpdateAccountInfo *) thr_user_data;
892 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
894 /* Get account and set it into mail_operation */
895 priv->account = g_object_ref (info->account);
898 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
899 * show any updates unless we do that
901 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
902 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
904 /* Get all the folders. We can do it synchronously because
905 we're already running in a different thread than the UI */
906 all_folders = tny_simple_list_new ();
907 query = tny_folder_store_query_new ();
908 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
909 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
914 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
918 iter = tny_list_create_iterator (all_folders);
919 while (!tny_iterator_is_done (iter)) {
920 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
922 recurse_folders (folder, query, all_folders);
923 tny_iterator_next (iter);
925 g_object_unref (G_OBJECT (iter));
927 /* Update status and notify. We need to call the notification
928 with a source function in order to call it from the main
929 loop. We need that in order not to get into trouble with
930 Gtk+. We use a timeout in order to provide more status
931 information, because the sync tinymail call does not
932 provide it for the moment */
933 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
935 /* Refresh folders */
936 new_headers = g_ptr_array_new ();
937 iter = tny_list_create_iterator (all_folders);
939 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
941 InternalFolderObserver *observer;
942 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
944 /* Refresh the folder */
945 /* Our observer receives notification of new emails during folder refreshes,
946 * so we can use observer->new_headers.
947 * TODO: This does not seem to be providing accurate numbers.
948 * Possibly the observer is notified asynchronously.
950 observer = g_object_new (internal_folder_observer_get_type (), NULL);
951 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
953 /* This gets the status information (headers) from the server.
954 * We use the blocking version, because we are already in a separate
958 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
959 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
962 /* If the retrieve type is full messages, refresh and get the messages */
963 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
965 iter = tny_list_create_iterator (observer->new_headers);
966 while (!tny_iterator_is_done (iter)) {
967 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
969 /* Apply per-message size limits */
970 if (tny_header_get_message_size (header) < info->max_size)
971 g_ptr_array_add (new_headers, g_object_ref (header));
973 g_object_unref (header);
974 tny_iterator_next (iter);
976 g_object_unref (iter);
978 /* We do not need to do it the first time
979 because it's automatically done by the tree
981 if (G_UNLIKELY (!first_time))
982 tny_folder_poke_status (TNY_FOLDER (folder));
984 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
985 g_object_unref (observer);
989 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
991 g_object_unref (G_OBJECT (folder));
992 tny_iterator_next (iter);
995 did_a_cancel = FALSE;
997 g_object_unref (G_OBJECT (iter));
998 g_source_remove (timeout);
1000 if (new_headers->len > 0) {
1004 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1006 /* Apply message count limit */
1007 /* If the number of messages exceeds the maximum, ask the
1008 * user to download them all,
1009 * as per the UI spec "Retrieval Limits" section in 4.4:
1011 printf ("************************** DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__,
1012 tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
1013 if (new_headers->len > info->retrieve_limit) {
1014 /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded,
1015 * with 'Get all' and 'Newest only' buttons. */
1016 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1017 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1018 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1019 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1020 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1025 priv->total = MIN (new_headers->len, info->retrieve_limit);
1026 while (msg_num < priv->total) {
1028 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1029 TnyFolder *folder = tny_header_get_folder (header);
1030 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1031 ModestMailOperationState *state;
1035 /* We can not just use the mail operation because the
1036 values of done and total could change before the
1038 state = modest_mail_operation_clone_state (info->mail_op);
1039 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1040 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1041 pair, (GDestroyNotify) modest_pair_free);
1043 g_object_unref (msg);
1044 g_object_unref (folder);
1048 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1049 g_ptr_array_free (new_headers, FALSE);
1053 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1056 if (priv->account != NULL)
1057 g_object_unref (priv->account);
1058 priv->account = g_object_ref (info->transport_account);
1060 send_queue = modest_runtime_get_send_queue (info->transport_account);
1062 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1063 modest_tny_send_queue_try_to_send (send_queue);
1064 g_source_remove (timeout);
1066 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1067 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1068 "cannot create a send queue for %s\n",
1069 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1070 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1073 /* Check if the operation was a success */
1075 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1077 /* Update the last updated key */
1078 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1079 set_last_updated_idle,
1080 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1081 (GDestroyNotify) g_free);
1085 /* Notify about operation end. Note that the info could be
1086 freed before this idle happens, but the mail operation will
1088 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1091 g_object_unref (query);
1092 g_object_unref (all_folders);
1093 g_object_unref (info->account);
1094 g_object_unref (info->transport_account);
1095 g_free (info->retrieve_type);
1096 g_slice_free (UpdateAccountInfo, info);
1104 modest_mail_operation_update_account (ModestMailOperation *self,
1105 const gchar *account_name)
1108 UpdateAccountInfo *info;
1109 ModestMailOperationPrivate *priv;
1110 ModestAccountMgr *mgr;
1111 TnyStoreAccount *modest_account;
1112 TnyTransportAccount *transport_account;
1114 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1115 g_return_val_if_fail (account_name, FALSE);
1117 /* Make sure that we have a connection, and request one
1119 * TODO: Is there some way to trigger this for every attempt to
1120 * use the network? */
1121 if (!modest_platform_connect_and_wait(NULL))
1124 /* Init mail operation. Set total and done to 0, and do not
1125 update them, this way the progress objects will know that
1126 we have no clue about the number of the objects */
1127 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1130 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1132 /* Get the Modest account */
1133 modest_account = (TnyStoreAccount *)
1134 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1136 TNY_ACCOUNT_TYPE_STORE);
1138 if (!modest_account) {
1139 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1140 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1141 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1142 "cannot get tny store account for %s\n", account_name);
1143 modest_mail_operation_notify_end (self);
1149 /* Get the transport account, we can not do it in the thread
1150 due to some problems with dbus */
1151 transport_account = (TnyTransportAccount *)
1152 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1154 if (!transport_account) {
1155 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1156 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1157 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1158 "cannot get tny transport account for %s\n", account_name);
1159 modest_mail_operation_notify_end (self);
1164 /* Create the helper object */
1165 info = g_slice_new (UpdateAccountInfo);
1166 info->mail_op = self;
1167 info->account = modest_account;
1168 info->transport_account = transport_account;
1170 /* Get the message size limit */
1171 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1172 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1173 if (info->max_size == 0)
1174 info->max_size = G_MAXINT;
1176 info->max_size = info->max_size * KB;
1178 /* Get per-account retrieval type */
1179 mgr = modest_runtime_get_account_mgr ();
1180 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1181 MODEST_ACCOUNT_RETRIEVE, FALSE);
1183 /* Get per-account message amount retrieval limit */
1184 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1185 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1186 if (info->retrieve_limit == 0)
1187 info->retrieve_limit = G_MAXINT;
1189 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1191 /* Set account busy */
1192 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1193 priv->account_name = g_strdup(account_name);
1195 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1200 /* ******************************************************************* */
1201 /* ************************** STORE ACTIONS ************************* */
1202 /* ******************************************************************* */
1206 modest_mail_operation_create_folder (ModestMailOperation *self,
1207 TnyFolderStore *parent,
1210 ModestMailOperationPrivate *priv;
1211 TnyFolder *new_folder = NULL;
1213 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1214 g_return_val_if_fail (name, NULL);
1216 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1219 if (TNY_IS_FOLDER (parent)) {
1220 /* Check folder rules */
1221 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1222 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1223 /* Set status failed and set an error */
1224 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1225 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1226 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1227 _("mail_in_ui_folder_create_error"));
1232 /* Create the folder */
1233 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1234 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1236 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1239 /* Notify about operation end */
1240 modest_mail_operation_notify_end (self);
1246 modest_mail_operation_remove_folder (ModestMailOperation *self,
1248 gboolean remove_to_trash)
1250 TnyAccount *account;
1251 ModestMailOperationPrivate *priv;
1252 ModestTnyFolderRules rules;
1254 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1255 g_return_if_fail (TNY_IS_FOLDER (folder));
1257 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1259 /* Check folder rules */
1260 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1261 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1262 /* Set status failed and set an error */
1263 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1264 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1265 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1266 _("mail_in_ui_folder_delete_error"));
1270 /* Get the account */
1271 account = modest_tny_folder_get_account (folder);
1272 priv->account = g_object_ref(account);
1274 /* Delete folder or move to trash */
1275 if (remove_to_trash) {
1276 TnyFolder *trash_folder = NULL;
1277 trash_folder = modest_tny_account_get_special_folder (account,
1278 TNY_FOLDER_TYPE_TRASH);
1279 /* TODO: error_handling */
1280 modest_mail_operation_xfer_folder (self, folder,
1281 TNY_FOLDER_STORE (trash_folder), TRUE);
1283 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1285 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1286 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1289 g_object_unref (G_OBJECT (parent));
1291 g_object_unref (G_OBJECT (account));
1294 /* Notify about operation end */
1295 modest_mail_operation_notify_end (self);
1299 transfer_folder_status_cb (GObject *obj,
1303 ModestMailOperation *self;
1304 ModestMailOperationPrivate *priv;
1305 ModestMailOperationState *state;
1307 g_return_if_fail (status != NULL);
1308 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1310 self = MODEST_MAIL_OPERATION (user_data);
1311 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1313 if ((status->position == 1) && (status->of_total == 100))
1316 priv->done = status->position;
1317 priv->total = status->of_total;
1319 state = modest_mail_operation_clone_state (self);
1320 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1321 g_slice_free (ModestMailOperationState, state);
1326 transfer_folder_cb (TnyFolder *folder,
1327 TnyFolderStore *into,
1329 TnyFolder *new_folder, GError **err,
1332 ModestMailOperation *self = NULL;
1333 ModestMailOperationPrivate *priv = NULL;
1335 self = MODEST_MAIL_OPERATION (user_data);
1337 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1340 priv->error = g_error_copy (*err);
1342 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1343 } else if (cancelled) {
1344 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1345 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1346 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1347 _("Transference of %s was cancelled."),
1348 tny_folder_get_name (folder));
1351 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1355 g_object_unref (folder);
1356 g_object_unref (into);
1357 if (new_folder != NULL)
1358 g_object_unref (new_folder);
1360 /* Notify about operation end */
1361 modest_mail_operation_notify_end (self);
1365 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1367 TnyFolderStore *parent,
1368 gboolean delete_original)
1370 ModestMailOperationPrivate *priv = NULL;
1371 ModestTnyFolderRules parent_rules, rules;
1373 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1374 g_return_if_fail (TNY_IS_FOLDER (folder));
1375 g_return_if_fail (TNY_IS_FOLDER (parent));
1377 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1379 /* Get account and set it into mail_operation */
1380 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1381 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1383 /* Get folder rules */
1384 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1385 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1387 if (!TNY_IS_FOLDER_STORE (parent)) {
1391 /* The moveable restriction is applied also to copy operation */
1392 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1393 /* Set status failed and set an error */
1394 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1395 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1396 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1397 _("mail_in_ui_folder_move_target_error"));
1399 /* Notify the queue */
1400 modest_mail_operation_notify_end (self);
1401 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1402 /* Set status failed and set an error */
1403 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1404 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1405 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1406 _("FIXME: parent folder does not accept new folders"));
1408 /* Notify the queue */
1409 modest_mail_operation_notify_end (self);
1411 /* Pick references for async calls */
1412 g_object_ref (folder);
1413 g_object_ref (parent);
1415 /* Move/Copy folder */
1416 tny_folder_copy_async (folder,
1418 tny_folder_get_name (folder),
1421 transfer_folder_status_cb,
1427 modest_mail_operation_rename_folder (ModestMailOperation *self,
1431 ModestMailOperationPrivate *priv;
1432 ModestTnyFolderRules rules;
1434 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1435 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1436 g_return_if_fail (name);
1438 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1440 /* Get account and set it into mail_operation */
1441 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1443 /* Check folder rules */
1444 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1445 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1446 /* Set status failed and set an error */
1447 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1448 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1449 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1450 _("FIXME: unable to rename"));
1452 /* Notify about operation end */
1453 modest_mail_operation_notify_end (self);
1455 /* Rename. Camel handles folder subscription/unsubscription */
1456 TnyFolderStore *into;
1458 into = tny_folder_get_folder_store (folder);
1459 tny_folder_copy_async (folder, into, name, TRUE,
1461 transfer_folder_status_cb,
1464 g_object_unref (into);
1469 /* ******************************************************************* */
1470 /* ************************** MSG ACTIONS ************************* */
1471 /* ******************************************************************* */
1473 void modest_mail_operation_get_msg (ModestMailOperation *self,
1475 GetMsgAsyncUserCallback user_callback,
1478 GetMsgAsyncHelper *helper = NULL;
1480 ModestMailOperationPrivate *priv;
1482 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1483 g_return_if_fail (TNY_IS_HEADER (header));
1485 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1486 folder = tny_header_get_folder (header);
1488 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1490 /* Get message from folder */
1492 /* Get account and set it into mail_operation */
1493 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1495 helper = g_slice_new0 (GetMsgAsyncHelper);
1496 helper->mail_op = self;
1497 helper->user_callback = user_callback;
1498 helper->user_data = user_data;
1499 helper->header = g_object_ref (header);
1501 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1503 g_object_unref (G_OBJECT (folder));
1505 /* Set status failed and set an error */
1506 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1507 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1508 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1509 _("Error trying to get a message. No folder found for header"));
1511 /* Notify the queue */
1512 modest_mail_operation_notify_end (self);
1517 get_msg_cb (TnyFolder *folder,
1523 GetMsgAsyncHelper *helper = NULL;
1524 ModestMailOperation *self = NULL;
1525 ModestMailOperationPrivate *priv = NULL;
1527 helper = (GetMsgAsyncHelper *) user_data;
1528 g_return_if_fail (helper != NULL);
1529 self = helper->mail_op;
1530 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1531 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1533 /* Check errors and cancel */
1535 priv->error = g_error_copy (*error);
1536 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1540 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1541 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1542 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1543 _("Error trying to refresh the contents of %s"),
1544 tny_folder_get_name (folder));
1548 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1550 /* If user defined callback function was defined, call it */
1551 if (helper->user_callback) {
1552 helper->user_callback (self, helper->header, msg, helper->user_data);
1557 g_object_unref (helper->header);
1558 g_slice_free (GetMsgAsyncHelper, helper);
1560 /* Notify about operation end */
1561 modest_mail_operation_notify_end (self);
1565 get_msg_status_cb (GObject *obj,
1569 GetMsgAsyncHelper *helper = NULL;
1570 ModestMailOperation *self;
1571 ModestMailOperationPrivate *priv;
1572 ModestMailOperationState *state;
1574 g_return_if_fail (status != NULL);
1575 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1577 helper = (GetMsgAsyncHelper *) user_data;
1578 g_return_if_fail (helper != NULL);
1580 self = helper->mail_op;
1581 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1583 if ((status->position == 1) && (status->of_total == 100))
1589 state = modest_mail_operation_clone_state (self);
1590 state->bytes_done = status->position;
1591 state->bytes_total = status->of_total;
1592 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1593 g_slice_free (ModestMailOperationState, state);
1596 /****************************************************/
1598 ModestMailOperation *mail_op;
1600 GetMsgAsyncUserCallback user_callback;
1602 GDestroyNotify notify;
1606 GetMsgAsyncUserCallback user_callback;
1610 ModestMailOperation *mail_op;
1611 } NotifyGetMsgsInfo;
1615 * Used by get_msgs_full_thread to call the user_callback for each
1616 * message that has been read
1619 notify_get_msgs_full (gpointer data)
1621 NotifyGetMsgsInfo *info;
1623 info = (NotifyGetMsgsInfo *) data;
1625 /* Call the user callback */
1626 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1628 g_slice_free (NotifyGetMsgsInfo, info);
1634 * Used by get_msgs_full_thread to free al the thread resources and to
1635 * call the destroy function for the passed user_data
1638 get_msgs_full_destroyer (gpointer data)
1640 GetFullMsgsInfo *info;
1642 info = (GetFullMsgsInfo *) data;
1645 info->notify (info->user_data);
1648 g_object_unref (info->headers);
1649 g_slice_free (GetFullMsgsInfo, info);
1655 get_msgs_full_thread (gpointer thr_user_data)
1657 GetFullMsgsInfo *info;
1658 ModestMailOperationPrivate *priv = NULL;
1659 TnyIterator *iter = NULL;
1661 info = (GetFullMsgsInfo *) thr_user_data;
1662 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1664 iter = tny_list_create_iterator (info->headers);
1665 while (!tny_iterator_is_done (iter)) {
1669 header = TNY_HEADER (tny_iterator_get_current (iter));
1670 folder = tny_header_get_folder (header);
1672 /* Get message from folder */
1675 /* The callback will call it per each header */
1676 msg = tny_folder_get_msg (folder, header, &(priv->error));
1679 ModestMailOperationState *state;
1684 /* notify progress */
1685 state = modest_mail_operation_clone_state (info->mail_op);
1686 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1687 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1688 pair, (GDestroyNotify) modest_pair_free);
1690 /* The callback is the responsible for
1691 freeing the message */
1692 if (info->user_callback) {
1693 NotifyGetMsgsInfo *info_notify;
1694 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1695 info_notify->user_callback = info->user_callback;
1696 info_notify->mail_op = info->mail_op;
1697 info_notify->header = g_object_ref (header);
1698 info_notify->msg = g_object_ref (msg);
1699 info_notify->user_data = info->user_data;
1700 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1701 notify_get_msgs_full,
1704 g_object_unref (msg);
1707 /* Set status failed and set an error */
1708 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1709 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1710 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1711 "Error trying to get a message. No folder found for header");
1713 g_object_unref (header);
1714 tny_iterator_next (iter);
1717 /* Set operation status */
1718 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1719 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1721 /* Notify about operation end */
1722 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1724 /* Free thread resources. Will be called after all previous idles */
1725 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1731 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1732 TnyList *header_list,
1733 GetMsgAsyncUserCallback user_callback,
1735 GDestroyNotify notify)
1737 TnyHeader *header = NULL;
1738 TnyFolder *folder = NULL;
1740 ModestMailOperationPrivate *priv = NULL;
1741 GetFullMsgsInfo *info = NULL;
1742 gboolean size_ok = TRUE;
1744 TnyIterator *iter = NULL;
1746 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1748 /* Init mail operation */
1749 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1750 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1752 priv->total = tny_list_get_length(header_list);
1754 /* Get account and set it into mail_operation */
1755 if (tny_list_get_length (header_list) >= 1) {
1756 iter = tny_list_create_iterator (header_list);
1757 header = TNY_HEADER (tny_iterator_get_current (iter));
1758 folder = tny_header_get_folder (header);
1759 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1760 g_object_unref (header);
1761 g_object_unref (folder);
1763 if (tny_list_get_length (header_list) == 1) {
1764 g_object_unref (iter);
1769 /* Get msg size limit */
1770 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1771 MODEST_CONF_MSG_SIZE_LIMIT,
1774 g_clear_error (&(priv->error));
1775 max_size = G_MAXINT;
1777 max_size = max_size * KB;
1780 /* Check message size limits. If there is only one message
1781 always retrieve it */
1783 while (!tny_iterator_is_done (iter) && size_ok) {
1784 header = TNY_HEADER (tny_iterator_get_current (iter));
1785 if (tny_header_get_message_size (header) >= max_size)
1787 g_object_unref (header);
1788 tny_iterator_next (iter);
1790 g_object_unref (iter);
1794 /* Create the info */
1795 info = g_slice_new0 (GetFullMsgsInfo);
1796 info->mail_op = self;
1797 info->user_callback = user_callback;
1798 info->user_data = user_data;
1799 info->headers = g_object_ref (header_list);
1800 info->notify = notify;
1802 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1804 /* Set status failed and set an error */
1805 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1806 /* FIXME: the error msg is different for pop */
1807 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1808 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1809 _("emev_ni_ui_imap_msg_size_exceed_error"));
1810 /* Remove from queue and free resources */
1811 modest_mail_operation_notify_end (self);
1819 modest_mail_operation_remove_msg (ModestMailOperation *self,
1821 gboolean remove_to_trash)
1824 ModestMailOperationPrivate *priv;
1826 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1827 g_return_if_fail (TNY_IS_HEADER (header));
1829 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1830 folder = tny_header_get_folder (header);
1832 /* Get account and set it into mail_operation */
1833 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1835 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1837 /* Delete or move to trash */
1838 if (remove_to_trash) {
1839 TnyFolder *trash_folder;
1840 TnyStoreAccount *store_account;
1842 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1843 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1844 TNY_FOLDER_TYPE_TRASH);
1849 headers = tny_simple_list_new ();
1850 tny_list_append (headers, G_OBJECT (header));
1851 g_object_unref (header);
1854 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1855 g_object_unref (headers);
1856 /* g_object_unref (trash_folder); */
1858 ModestMailOperationPrivate *priv;
1860 /* Set status failed and set an error */
1861 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1862 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1863 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1864 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1865 _("Error trying to delete a message. Trash folder not found"));
1868 g_object_unref (G_OBJECT (store_account));
1870 tny_folder_remove_msg (folder, header, &(priv->error));
1872 tny_folder_sync(folder, TRUE, &(priv->error));
1877 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1879 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1882 g_object_unref (G_OBJECT (folder));
1884 /* Notify about operation end */
1885 modest_mail_operation_notify_end (self);
1889 transfer_msgs_status_cb (GObject *obj,
1893 XFerMsgAsyncHelper *helper = NULL;
1894 ModestMailOperation *self;
1895 ModestMailOperationPrivate *priv;
1896 ModestMailOperationState *state;
1899 g_return_if_fail (status != NULL);
1900 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1902 helper = (XFerMsgAsyncHelper *) user_data;
1903 g_return_if_fail (helper != NULL);
1905 self = helper->mail_op;
1906 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1908 if ((status->position == 1) && (status->of_total == 100))
1911 priv->done = status->position;
1912 priv->total = status->of_total;
1914 state = modest_mail_operation_clone_state (self);
1915 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1916 g_slice_free (ModestMailOperationState, state);
1921 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1923 XFerMsgAsyncHelper *helper;
1924 ModestMailOperation *self;
1925 ModestMailOperationPrivate *priv;
1927 helper = (XFerMsgAsyncHelper *) user_data;
1928 self = helper->mail_op;
1930 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1933 priv->error = g_error_copy (*err);
1935 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1936 } else if (cancelled) {
1937 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1938 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1939 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1940 _("Error trying to refresh the contents of %s"),
1941 tny_folder_get_name (folder));
1944 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1947 /* If user defined callback function was defined, call it */
1948 if (helper->user_callback) {
1949 helper->user_callback (priv->source, helper->user_data);
1953 g_object_unref (helper->headers);
1954 g_object_unref (helper->dest_folder);
1955 g_object_unref (helper->mail_op);
1956 g_slice_free (XFerMsgAsyncHelper, helper);
1957 g_object_unref (folder);
1959 /* Notify about operation end */
1960 modest_mail_operation_notify_end (self);
1964 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1967 gboolean delete_original,
1968 XferMsgsAsynUserCallback user_callback,
1971 ModestMailOperationPrivate *priv;
1973 TnyFolder *src_folder;
1974 XFerMsgAsyncHelper *helper;
1976 ModestTnyFolderRules rules;
1978 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1979 g_return_if_fail (TNY_IS_LIST (headers));
1980 g_return_if_fail (TNY_IS_FOLDER (folder));
1982 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1985 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1987 /* Apply folder rules */
1988 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1990 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1991 /* Set status failed and set an error */
1992 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1993 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1994 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1995 _("FIXME: folder does not accept msgs"));
1996 /* Notify the queue */
1997 modest_mail_operation_notify_end (self);
2001 /* Create the helper */
2002 helper = g_slice_new0 (XFerMsgAsyncHelper);
2003 helper->mail_op = g_object_ref(self);
2004 helper->dest_folder = g_object_ref(folder);
2005 helper->headers = g_object_ref(headers);
2006 helper->user_callback = user_callback;
2007 helper->user_data = user_data;
2009 /* Get source folder */
2010 iter = tny_list_create_iterator (headers);
2011 header = TNY_HEADER (tny_iterator_get_current (iter));
2012 src_folder = tny_header_get_folder (header);
2013 g_object_unref (header);
2014 g_object_unref (iter);
2016 /* Get account and set it into mail_operation */
2017 priv->account = modest_tny_folder_get_account (src_folder);
2019 /* Transfer messages */
2020 tny_folder_transfer_msgs_async (src_folder,
2025 transfer_msgs_status_cb,
2031 on_refresh_folder (TnyFolder *folder,
2036 RefreshAsyncHelper *helper = NULL;
2037 ModestMailOperation *self = NULL;
2038 ModestMailOperationPrivate *priv = NULL;
2040 helper = (RefreshAsyncHelper *) user_data;
2041 self = helper->mail_op;
2042 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2045 priv->error = g_error_copy (*error);
2046 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2051 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2052 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2053 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2054 _("Error trying to refresh the contents of %s"),
2055 tny_folder_get_name (folder));
2059 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2062 /* Call user defined callback, if it exists */
2063 if (helper->user_callback)
2064 helper->user_callback (priv->source, folder, helper->user_data);
2067 g_object_unref (helper->mail_op);
2068 g_slice_free (RefreshAsyncHelper, helper);
2069 g_object_unref (folder);
2071 /* Notify about operation end */
2072 modest_mail_operation_notify_end (self);
2076 on_refresh_folder_status_update (GObject *obj,
2080 RefreshAsyncHelper *helper = NULL;
2081 ModestMailOperation *self = NULL;
2082 ModestMailOperationPrivate *priv = NULL;
2083 ModestMailOperationState *state;
2085 g_return_if_fail (user_data != NULL);
2086 g_return_if_fail (status != NULL);
2087 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2089 helper = (RefreshAsyncHelper *) user_data;
2090 self = helper->mail_op;
2091 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2093 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2095 priv->done = status->position;
2096 priv->total = status->of_total;
2098 state = modest_mail_operation_clone_state (self);
2099 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2100 g_slice_free (ModestMailOperationState, state);
2104 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2106 RefreshAsyncUserCallback user_callback,
2109 ModestMailOperationPrivate *priv = NULL;
2110 RefreshAsyncHelper *helper = NULL;
2112 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2114 /* Pick a reference */
2115 g_object_ref (folder);
2117 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2119 /* Get account and set it into mail_operation */
2120 priv->account = modest_tny_folder_get_account (folder);
2122 /* Create the helper */
2123 helper = g_slice_new0 (RefreshAsyncHelper);
2124 helper->mail_op = g_object_ref(self);
2125 helper->user_callback = user_callback;
2126 helper->user_data = user_data;
2128 /* Refresh the folder. TODO: tinymail could issue a status
2129 updates before the callback call then this could happen. We
2130 must review the design */
2131 tny_folder_refresh_async (folder,
2133 on_refresh_folder_status_update,
2139 * It's used by the mail operation queue to notify the observers
2140 * attached to that signal that the operation finished. We need to use
2141 * that because tinymail does not give us the progress of a given
2142 * operation when it finishes (it directly calls the operation
2146 modest_mail_operation_notify_end (ModestMailOperation *self)
2148 ModestMailOperationState *state;
2149 ModestMailOperationPrivate *priv = NULL;
2151 g_return_if_fail (self);
2153 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2156 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2160 /* Set the account back to not busy */
2161 if (priv->account_name) {
2162 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2164 g_free(priv->account_name);
2165 priv->account_name = NULL;
2168 /* Notify the observers about the mail opertation end */
2169 state = modest_mail_operation_clone_state (self);
2170 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2171 g_slice_free (ModestMailOperationState, state);