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__);
371 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
373 /* This emits progress-changed on which the mail operation queue is
374 * listening, so the mail operation is correctly removed from the
375 * queue without further explicit calls. */
376 modest_mail_operation_notify_end (self);
382 modest_mail_operation_get_task_done (ModestMailOperation *self)
384 ModestMailOperationPrivate *priv;
386 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
388 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
393 modest_mail_operation_get_task_total (ModestMailOperation *self)
395 ModestMailOperationPrivate *priv;
397 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
399 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
404 modest_mail_operation_is_finished (ModestMailOperation *self)
406 ModestMailOperationPrivate *priv;
407 gboolean retval = FALSE;
409 if (!MODEST_IS_MAIL_OPERATION (self)) {
410 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
414 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
416 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
417 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
418 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
429 modest_mail_operation_get_id (ModestMailOperation *self)
431 ModestMailOperationPrivate *priv;
433 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
435 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
440 modest_mail_operation_set_id (ModestMailOperation *self,
443 ModestMailOperationPrivate *priv;
445 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
447 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
452 * Creates an image of the current state of a mail operation, the
453 * caller must free it
455 static ModestMailOperationState *
456 modest_mail_operation_clone_state (ModestMailOperation *self)
458 ModestMailOperationState *state;
459 ModestMailOperationPrivate *priv;
461 /* FIXME: this should be fixed properly
463 * in some cases, priv was NULL, so checking here to
466 g_return_val_if_fail (self, NULL);
467 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
468 g_return_val_if_fail (priv, NULL);
473 state = g_slice_new (ModestMailOperationState);
475 state->status = priv->status;
476 state->op_type = priv->op_type;
477 state->done = priv->done;
478 state->total = priv->total;
479 state->finished = modest_mail_operation_is_finished (self);
480 state->bytes_done = 0;
481 state->bytes_total = 0;
486 /* ******************************************************************* */
487 /* ************************** SEND ACTIONS ************************* */
488 /* ******************************************************************* */
491 modest_mail_operation_send_mail (ModestMailOperation *self,
492 TnyTransportAccount *transport_account,
495 TnySendQueue *send_queue = NULL;
496 ModestMailOperationPrivate *priv;
498 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
499 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
500 g_return_if_fail (TNY_IS_MSG (msg));
502 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
504 /* Get account and set it into mail_operation */
505 priv->account = g_object_ref (transport_account);
509 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
510 if (!TNY_IS_SEND_QUEUE(send_queue)) {
511 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
512 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
513 "modest: could not find send queue for account\n");
515 /* TODO: connect to the msg-sent in order to know when
516 the mail operation is finished */
517 tny_send_queue_add (send_queue, msg, &(priv->error));
518 /* TODO: we're setting always success, do the check in
520 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
523 /* TODO: do this in the handler of the "msg-sent"
524 signal.Notify about operation end */
525 modest_mail_operation_notify_end (self);
529 modest_mail_operation_send_new_mail (ModestMailOperation *self,
530 TnyTransportAccount *transport_account,
532 const gchar *from, const gchar *to,
533 const gchar *cc, const gchar *bcc,
534 const gchar *subject, const gchar *plain_body,
535 const gchar *html_body,
536 const GList *attachments_list,
537 TnyHeaderFlags priority_flags)
539 TnyMsg *new_msg = NULL;
540 TnyFolder *folder = NULL;
541 TnyHeader *header = NULL;
542 ModestMailOperationPrivate *priv = NULL;
544 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
547 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549 /* Check parametters */
551 /* Set status failed and set an error */
552 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
553 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
554 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
555 _("Error trying to send a mail. You need to set at least one recipient"));
559 if (html_body == NULL) {
560 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
562 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
565 g_printerr ("modest: failed to create a new msg\n");
569 /* Set priority flags in message */
570 header = tny_msg_get_header (new_msg);
571 if (priority_flags != 0)
572 tny_header_set_flags (header, priority_flags);
574 /* Call mail operation */
575 modest_mail_operation_send_mail (self, transport_account, new_msg);
577 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
579 if (draft_msg != NULL) {
580 header = tny_msg_get_header (draft_msg);
581 /* Note: This can fail (with a warning) if the message is not really already in a folder,
582 * because this function requires it to have a UID. */
583 tny_folder_remove_msg (folder, header, NULL);
584 g_object_unref (header);
589 g_object_unref (G_OBJECT (new_msg));
593 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
594 TnyTransportAccount *transport_account,
596 const gchar *from, const gchar *to,
597 const gchar *cc, const gchar *bcc,
598 const gchar *subject, const gchar *plain_body,
599 const gchar *html_body,
600 const GList *attachments_list,
601 TnyHeaderFlags priority_flags)
604 TnyFolder *folder = NULL;
605 TnyHeader *header = NULL;
606 ModestMailOperationPrivate *priv = NULL;
608 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
609 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
611 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
613 /* Get account and set it into mail_operation */
614 priv->account = g_object_ref (transport_account);
616 if (html_body == NULL) {
617 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
619 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
622 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
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 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
636 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
637 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
638 "modest: failed to create a new msg\n");
642 if (draft_msg != NULL) {
643 header = tny_msg_get_header (draft_msg);
644 /* Remove the old draft expunging it */
645 tny_folder_remove_msg (folder, header, NULL);
646 tny_folder_sync (folder, TRUE, NULL);
647 g_object_unref (header);
650 tny_folder_add_msg (folder, msg, &(priv->error));
652 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
656 g_object_unref (G_OBJECT(msg));
658 g_object_unref (G_OBJECT(folder));
660 modest_mail_operation_notify_end (self);
665 ModestMailOperation *mail_op;
666 TnyStoreAccount *account;
667 TnyTransportAccount *transport_account;
670 gchar *retrieve_type;
672 UpdateAccountCallback callback;
676 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
677 /* We use this folder observer to track the headers that have been
678 * added to a folder */
681 TnyList *new_headers;
682 } InternalFolderObserver;
686 } InternalFolderObserverClass;
688 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
690 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
691 internal_folder_observer,
693 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
697 foreach_add_item (gpointer header, gpointer user_data)
699 /* printf("DEBUG: %s: header subject=%s\n",
700 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
702 tny_list_prepend (TNY_LIST (user_data),
703 g_object_ref (G_OBJECT (header)));
706 /* This is the method that looks for new messages in a folder */
708 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
710 InternalFolderObserver *derived = (InternalFolderObserver *)self;
712 TnyFolderChangeChanged changed;
714 changed = tny_folder_change_get_changed (change);
716 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
719 /* Get added headers */
720 list = tny_simple_list_new ();
721 tny_folder_change_get_added_headers (change, list);
723 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
724 * __FUNCTION__, tny_list_get_length(list));
727 /* Add them to the folder observer */
728 tny_list_foreach (list, foreach_add_item,
729 derived->new_headers);
731 g_object_unref (G_OBJECT (list));
736 internal_folder_observer_init (InternalFolderObserver *self)
738 self->new_headers = tny_simple_list_new ();
741 internal_folder_observer_finalize (GObject *object)
743 InternalFolderObserver *self;
745 self = (InternalFolderObserver *) object;
746 g_object_unref (self->new_headers);
748 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
751 tny_folder_observer_init (TnyFolderObserverIface *iface)
753 iface->update_func = internal_folder_observer_update;
756 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
758 GObjectClass *object_class;
760 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
761 object_class = (GObjectClass*) klass;
762 object_class->finalize = internal_folder_observer_finalize;
768 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
771 TnyList *folders = tny_simple_list_new ();
773 tny_folder_store_get_folders (store, folders, query, NULL);
774 iter = tny_list_create_iterator (folders);
776 while (!tny_iterator_is_done (iter)) {
778 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
780 tny_list_prepend (all_folders, G_OBJECT (folder));
781 recurse_folders (folder, query, all_folders);
782 g_object_unref (G_OBJECT (folder));
784 tny_iterator_next (iter);
786 g_object_unref (G_OBJECT (iter));
787 g_object_unref (G_OBJECT (folders));
791 * Issues the "progress-changed" signal. The timer won't be removed,
792 * so you must call g_source_remove to stop the signal emission
795 idle_notify_progress (gpointer data)
797 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
798 ModestMailOperationState *state;
800 state = modest_mail_operation_clone_state (mail_op);
801 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
802 g_slice_free (ModestMailOperationState, state);
808 * Issues the "progress-changed" signal and removes the timer. It uses
809 * a lock to ensure that the progress information of the mail
810 * operation is not modified while there are notifications pending
813 idle_notify_progress_once (gpointer data)
817 pair = (ModestPair *) data;
819 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
821 /* Free the state and the reference to the mail operation */
822 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
823 g_object_unref (pair->first);
829 * Used by update_account_thread to notify the queue from the main
830 * loop. We call it inside an idle call to achieve that
833 notify_update_account_queue (gpointer data)
835 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
836 ModestMailOperationPrivate *priv = NULL;
838 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
840 modest_mail_operation_notify_end (mail_op);
841 g_object_unref (mail_op);
847 compare_headers_by_date (gconstpointer a,
850 TnyHeader **header1, **header2;
853 header1 = (TnyHeader **) a;
854 header2 = (TnyHeader **) b;
856 sent1 = tny_header_get_date_sent (*header1);
857 sent2 = tny_header_get_date_sent (*header2);
859 /* We want the most recent ones (greater time_t) at the
868 set_last_updated_idle (gpointer data)
870 /* It does not matter if the time is not exactly the same than
871 the time when this idle was called, it's just an
872 approximation and it won't be very different */
873 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
875 MODEST_ACCOUNT_LAST_UPDATED,
883 update_account_thread (gpointer thr_user_data)
885 static gboolean first_time = TRUE;
886 UpdateAccountInfo *info;
887 TnyList *all_folders = NULL;
888 GPtrArray *new_headers = NULL;
889 TnyIterator *iter = NULL;
890 TnyFolderStoreQuery *query = NULL;
891 ModestMailOperationPrivate *priv = NULL;
892 ModestTnySendQueue *send_queue = NULL;
894 info = (UpdateAccountInfo *) thr_user_data;
895 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
897 /* Get account and set it into mail_operation */
898 priv->account = g_object_ref (info->account);
901 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
902 * show any updates unless we do that
904 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
905 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
907 /* Get all the folders. We can do it synchronously because
908 we're already running in a different thread than the UI */
909 all_folders = tny_simple_list_new ();
910 query = tny_folder_store_query_new ();
911 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
912 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
917 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
921 iter = tny_list_create_iterator (all_folders);
922 while (!tny_iterator_is_done (iter)) {
923 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
925 recurse_folders (folder, query, all_folders);
926 tny_iterator_next (iter);
928 g_object_unref (G_OBJECT (iter));
930 /* Update status and notify. We need to call the notification
931 with a source function in order to call it from the main
932 loop. We need that in order not to get into trouble with
933 Gtk+. We use a timeout in order to provide more status
934 information, because the sync tinymail call does not
935 provide it for the moment */
936 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
938 /* Refresh folders */
939 new_headers = g_ptr_array_new ();
940 iter = tny_list_create_iterator (all_folders);
942 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
944 InternalFolderObserver *observer;
945 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
947 /* Refresh the folder */
948 /* Our observer receives notification of new emails during folder refreshes,
949 * so we can use observer->new_headers.
951 observer = g_object_new (internal_folder_observer_get_type (), NULL);
952 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
954 /* This gets the status information (headers) from the server.
955 * We use the blocking version, because we are already in a separate
959 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
960 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
963 /* If the retrieve type is full messages, refresh and get the messages */
964 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
966 iter = tny_list_create_iterator (observer->new_headers);
967 while (!tny_iterator_is_done (iter)) {
968 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
970 /* Apply per-message size limits */
971 if (tny_header_get_message_size (header) < info->max_size)
972 g_ptr_array_add (new_headers, g_object_ref (header));
974 g_object_unref (header);
975 tny_iterator_next (iter);
977 g_object_unref (iter);
979 /* We do not need to do it the first time
980 because it's automatically done by the tree
982 if (G_UNLIKELY (!first_time))
983 tny_folder_poke_status (TNY_FOLDER (folder));
985 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
986 g_object_unref (observer);
989 g_object_unref (G_OBJECT (folder));
992 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
996 tny_iterator_next (iter);
999 did_a_cancel = FALSE;
1001 g_object_unref (G_OBJECT (iter));
1002 g_source_remove (timeout);
1004 if (new_headers->len > 0) {
1008 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1010 /* Apply message count limit */
1011 /* If the number of messages exceeds the maximum, ask the
1012 * user to download them all,
1013 * as per the UI spec "Retrieval Limits" section in 4.4:
1015 if (new_headers->len > info->retrieve_limit) {
1016 /* TODO: Ask the user, instead of just
1018 * mail_nc_msg_count_limit_exceeded, with 'Get
1019 * all' and 'Newest only' buttons. */
1020 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1021 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1022 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1023 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1024 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1029 priv->total = MIN (new_headers->len, info->retrieve_limit);
1030 while (msg_num < priv->total) {
1032 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1033 TnyFolder *folder = tny_header_get_folder (header);
1034 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1035 ModestMailOperationState *state;
1039 /* We can not just use the mail operation because the
1040 values of done and total could change before the
1042 state = modest_mail_operation_clone_state (info->mail_op);
1043 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1044 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1045 pair, (GDestroyNotify) modest_pair_free);
1047 g_object_unref (msg);
1048 g_object_unref (folder);
1052 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1053 g_ptr_array_free (new_headers, FALSE);
1057 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1060 if (priv->account != NULL)
1061 g_object_unref (priv->account);
1062 priv->account = g_object_ref (info->transport_account);
1064 send_queue = modest_runtime_get_send_queue (info->transport_account);
1066 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1067 modest_tny_send_queue_try_to_send (send_queue);
1068 g_source_remove (timeout);
1070 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1071 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1072 "cannot create a send queue for %s\n",
1073 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1074 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1077 /* Check if the operation was a success */
1079 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1081 /* Update the last updated key */
1082 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1083 set_last_updated_idle,
1084 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1085 (GDestroyNotify) g_free);
1089 /* Notify about operation end. Note that the info could be
1090 freed before this idle happens, but the mail operation will
1092 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1095 info->callback (info->mail_op,
1096 (new_headers) ? new_headers->len : 0,
1100 g_object_unref (query);
1101 g_object_unref (all_folders);
1102 g_object_unref (info->account);
1103 g_object_unref (info->transport_account);
1104 g_free (info->retrieve_type);
1105 g_slice_free (UpdateAccountInfo, info);
1113 modest_mail_operation_update_account (ModestMailOperation *self,
1114 const gchar *account_name,
1115 UpdateAccountCallback callback,
1119 UpdateAccountInfo *info;
1120 ModestMailOperationPrivate *priv;
1121 ModestAccountMgr *mgr;
1122 TnyStoreAccount *modest_account;
1123 TnyTransportAccount *transport_account;
1125 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1126 g_return_val_if_fail (account_name, FALSE);
1128 /* Init mail operation. Set total and done to 0, and do not
1129 update them, this way the progress objects will know that
1130 we have no clue about the number of the objects */
1131 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1134 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1136 /* Make sure that we have a connection, and request one
1138 * TODO: Is there some way to trigger this for every attempt to
1139 * use the network? */
1140 if (!modest_platform_connect_and_wait(NULL))
1143 /* Get the Modest account */
1144 modest_account = (TnyStoreAccount *)
1145 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1147 TNY_ACCOUNT_TYPE_STORE);
1149 if (!modest_account) {
1150 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1151 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1152 "cannot get tny store account for %s\n", account_name);
1157 /* Get the transport account, we can not do it in the thread
1158 due to some problems with dbus */
1159 transport_account = (TnyTransportAccount *)
1160 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1162 if (!transport_account) {
1163 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1164 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1165 "cannot get tny transport account for %s\n", account_name);
1169 /* Create the helper object */
1170 info = g_slice_new (UpdateAccountInfo);
1171 info->mail_op = self;
1172 info->account = modest_account;
1173 info->transport_account = transport_account;
1174 info->callback = callback;
1175 info->user_data = user_data;
1177 /* Get the message size limit */
1178 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1179 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1180 if (info->max_size == 0)
1181 info->max_size = G_MAXINT;
1183 info->max_size = info->max_size * KB;
1185 /* Get per-account retrieval type */
1186 mgr = modest_runtime_get_account_mgr ();
1187 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1188 MODEST_ACCOUNT_RETRIEVE, FALSE);
1190 /* Get per-account message amount retrieval limit */
1191 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1192 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1193 if (info->retrieve_limit == 0)
1194 info->retrieve_limit = G_MAXINT;
1196 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1198 /* Set account busy */
1199 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1200 priv->account_name = g_strdup(account_name);
1202 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1207 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1209 callback (self, 0, user_data);
1210 modest_mail_operation_notify_end (self);
1214 /* ******************************************************************* */
1215 /* ************************** STORE ACTIONS ************************* */
1216 /* ******************************************************************* */
1220 modest_mail_operation_create_folder (ModestMailOperation *self,
1221 TnyFolderStore *parent,
1224 ModestMailOperationPrivate *priv;
1225 TnyFolder *new_folder = NULL;
1227 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1228 g_return_val_if_fail (name, NULL);
1230 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1233 if (TNY_IS_FOLDER (parent)) {
1234 /* Check folder rules */
1235 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1236 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1237 /* Set status failed and set an error */
1238 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1239 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1240 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1241 _("mail_in_ui_folder_create_error"));
1246 /* Create the folder */
1247 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1248 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1250 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1253 /* Notify about operation end */
1254 modest_mail_operation_notify_end (self);
1260 modest_mail_operation_remove_folder (ModestMailOperation *self,
1262 gboolean remove_to_trash)
1264 TnyAccount *account;
1265 ModestMailOperationPrivate *priv;
1266 ModestTnyFolderRules rules;
1268 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1269 g_return_if_fail (TNY_IS_FOLDER (folder));
1271 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1273 /* Check folder rules */
1274 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1275 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1276 /* Set status failed and set an error */
1277 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1278 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1279 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1280 _("mail_in_ui_folder_delete_error"));
1284 /* Get the account */
1285 account = modest_tny_folder_get_account (folder);
1286 priv->account = g_object_ref(account);
1288 /* Delete folder or move to trash */
1289 if (remove_to_trash) {
1290 TnyFolder *trash_folder = NULL;
1291 trash_folder = modest_tny_account_get_special_folder (account,
1292 TNY_FOLDER_TYPE_TRASH);
1293 /* TODO: error_handling */
1294 modest_mail_operation_xfer_folder (self, folder,
1295 TNY_FOLDER_STORE (trash_folder), TRUE);
1297 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1299 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1300 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1303 g_object_unref (G_OBJECT (parent));
1305 g_object_unref (G_OBJECT (account));
1308 /* Notify about operation end */
1309 modest_mail_operation_notify_end (self);
1313 transfer_folder_status_cb (GObject *obj,
1317 ModestMailOperation *self;
1318 ModestMailOperationPrivate *priv;
1319 ModestMailOperationState *state;
1321 g_return_if_fail (status != NULL);
1322 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1324 self = MODEST_MAIL_OPERATION (user_data);
1325 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1327 if ((status->position == 1) && (status->of_total == 100))
1330 priv->done = status->position;
1331 priv->total = status->of_total;
1333 state = modest_mail_operation_clone_state (self);
1334 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1335 g_slice_free (ModestMailOperationState, state);
1340 transfer_folder_cb (TnyFolder *folder,
1341 TnyFolderStore *into,
1343 TnyFolder *new_folder, GError **err,
1346 ModestMailOperation *self = NULL;
1347 ModestMailOperationPrivate *priv = NULL;
1349 self = MODEST_MAIL_OPERATION (user_data);
1351 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1354 priv->error = g_error_copy (*err);
1356 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1357 } else if (cancelled) {
1358 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1359 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1360 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1361 _("Transference of %s was cancelled."),
1362 tny_folder_get_name (folder));
1365 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1369 g_object_unref (folder);
1370 g_object_unref (into);
1371 if (new_folder != NULL)
1372 g_object_unref (new_folder);
1374 /* Notify about operation end */
1375 modest_mail_operation_notify_end (self);
1379 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1381 TnyFolderStore *parent,
1382 gboolean delete_original)
1384 ModestMailOperationPrivate *priv = NULL;
1385 ModestTnyFolderRules parent_rules, rules;
1387 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1388 g_return_if_fail (TNY_IS_FOLDER (folder));
1389 g_return_if_fail (TNY_IS_FOLDER (parent));
1391 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1393 /* Get account and set it into mail_operation */
1394 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1395 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1397 /* Get folder rules */
1398 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1399 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1401 if (!TNY_IS_FOLDER_STORE (parent)) {
1405 /* The moveable restriction is applied also to copy operation */
1406 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1407 /* Set status failed and set an error */
1408 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1409 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1410 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1411 _("mail_in_ui_folder_move_target_error"));
1413 /* Notify the queue */
1414 modest_mail_operation_notify_end (self);
1415 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1416 /* Set status failed and set an error */
1417 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1418 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1419 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1420 _("FIXME: parent folder does not accept new folders"));
1422 /* Notify the queue */
1423 modest_mail_operation_notify_end (self);
1425 /* Pick references for async calls */
1426 g_object_ref (folder);
1427 g_object_ref (parent);
1429 /* Move/Copy folder */
1430 tny_folder_copy_async (folder,
1432 tny_folder_get_name (folder),
1435 transfer_folder_status_cb,
1441 modest_mail_operation_rename_folder (ModestMailOperation *self,
1445 ModestMailOperationPrivate *priv;
1446 ModestTnyFolderRules rules;
1448 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1449 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1450 g_return_if_fail (name);
1452 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1454 /* Get account and set it into mail_operation */
1455 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1457 /* Check folder rules */
1458 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1459 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1460 /* Set status failed and set an error */
1461 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1462 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1463 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1464 _("FIXME: unable to rename"));
1466 /* Notify about operation end */
1467 modest_mail_operation_notify_end (self);
1469 /* Rename. Camel handles folder subscription/unsubscription */
1470 TnyFolderStore *into;
1472 into = tny_folder_get_folder_store (folder);
1473 tny_folder_copy_async (folder, into, name, TRUE,
1475 transfer_folder_status_cb,
1478 g_object_unref (into);
1483 /* ******************************************************************* */
1484 /* ************************** MSG ACTIONS ************************* */
1485 /* ******************************************************************* */
1487 void modest_mail_operation_get_msg (ModestMailOperation *self,
1489 GetMsgAsyncUserCallback user_callback,
1492 GetMsgAsyncHelper *helper = NULL;
1494 ModestMailOperationPrivate *priv;
1496 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1497 g_return_if_fail (TNY_IS_HEADER (header));
1499 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1500 folder = tny_header_get_folder (header);
1502 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1504 /* Get message from folder */
1506 /* Get account and set it into mail_operation */
1507 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1509 helper = g_slice_new0 (GetMsgAsyncHelper);
1510 helper->mail_op = self;
1511 helper->user_callback = user_callback;
1512 helper->user_data = user_data;
1513 helper->header = g_object_ref (header);
1515 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1517 g_object_unref (G_OBJECT (folder));
1519 /* Set status failed and set an error */
1520 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1521 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1522 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1523 _("Error trying to get a message. No folder found for header"));
1525 /* Notify the queue */
1526 modest_mail_operation_notify_end (self);
1531 get_msg_cb (TnyFolder *folder,
1537 GetMsgAsyncHelper *helper = NULL;
1538 ModestMailOperation *self = NULL;
1539 ModestMailOperationPrivate *priv = NULL;
1541 helper = (GetMsgAsyncHelper *) user_data;
1542 g_return_if_fail (helper != NULL);
1543 self = helper->mail_op;
1544 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1545 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1547 /* Check errors and cancel */
1549 priv->error = g_error_copy (*error);
1550 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1554 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1555 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1556 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1557 _("Error trying to refresh the contents of %s"),
1558 tny_folder_get_name (folder));
1562 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1564 /* If user defined callback function was defined, call it */
1565 if (helper->user_callback) {
1566 helper->user_callback (self, helper->header, msg, helper->user_data);
1571 g_object_unref (helper->header);
1572 g_slice_free (GetMsgAsyncHelper, helper);
1574 /* Notify about operation end */
1575 modest_mail_operation_notify_end (self);
1579 get_msg_status_cb (GObject *obj,
1583 GetMsgAsyncHelper *helper = NULL;
1584 ModestMailOperation *self;
1585 ModestMailOperationPrivate *priv;
1586 ModestMailOperationState *state;
1588 g_return_if_fail (status != NULL);
1589 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1591 helper = (GetMsgAsyncHelper *) user_data;
1592 g_return_if_fail (helper != NULL);
1594 self = helper->mail_op;
1595 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1597 if ((status->position == 1) && (status->of_total == 100))
1603 state = modest_mail_operation_clone_state (self);
1604 state->bytes_done = status->position;
1605 state->bytes_total = status->of_total;
1606 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1607 g_slice_free (ModestMailOperationState, state);
1610 /****************************************************/
1612 ModestMailOperation *mail_op;
1614 GetMsgAsyncUserCallback user_callback;
1616 GDestroyNotify notify;
1620 GetMsgAsyncUserCallback user_callback;
1624 ModestMailOperation *mail_op;
1625 } NotifyGetMsgsInfo;
1629 * Used by get_msgs_full_thread to call the user_callback for each
1630 * message that has been read
1633 notify_get_msgs_full (gpointer data)
1635 NotifyGetMsgsInfo *info;
1637 info = (NotifyGetMsgsInfo *) data;
1639 /* Call the user callback */
1640 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1642 g_slice_free (NotifyGetMsgsInfo, info);
1648 * Used by get_msgs_full_thread to free al the thread resources and to
1649 * call the destroy function for the passed user_data
1652 get_msgs_full_destroyer (gpointer data)
1654 GetFullMsgsInfo *info;
1656 info = (GetFullMsgsInfo *) data;
1659 info->notify (info->user_data);
1662 g_object_unref (info->headers);
1663 g_slice_free (GetFullMsgsInfo, info);
1669 get_msgs_full_thread (gpointer thr_user_data)
1671 GetFullMsgsInfo *info;
1672 ModestMailOperationPrivate *priv = NULL;
1673 TnyIterator *iter = NULL;
1675 info = (GetFullMsgsInfo *) thr_user_data;
1676 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1678 iter = tny_list_create_iterator (info->headers);
1679 while (!tny_iterator_is_done (iter)) {
1683 header = TNY_HEADER (tny_iterator_get_current (iter));
1684 folder = tny_header_get_folder (header);
1686 /* Get message from folder */
1689 /* The callback will call it per each header */
1690 msg = tny_folder_get_msg (folder, header, &(priv->error));
1693 ModestMailOperationState *state;
1698 /* notify progress */
1699 state = modest_mail_operation_clone_state (info->mail_op);
1700 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1701 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1702 pair, (GDestroyNotify) modest_pair_free);
1704 /* The callback is the responsible for
1705 freeing the message */
1706 if (info->user_callback) {
1707 NotifyGetMsgsInfo *info_notify;
1708 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1709 info_notify->user_callback = info->user_callback;
1710 info_notify->mail_op = info->mail_op;
1711 info_notify->header = g_object_ref (header);
1712 info_notify->msg = g_object_ref (msg);
1713 info_notify->user_data = info->user_data;
1714 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1715 notify_get_msgs_full,
1718 g_object_unref (msg);
1721 /* Set status failed and set an error */
1722 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1723 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1724 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1725 "Error trying to get a message. No folder found for header");
1727 g_object_unref (header);
1728 tny_iterator_next (iter);
1731 /* Set operation status */
1732 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1733 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1735 /* Notify about operation end */
1736 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1738 /* Free thread resources. Will be called after all previous idles */
1739 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1745 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1746 TnyList *header_list,
1747 GetMsgAsyncUserCallback user_callback,
1749 GDestroyNotify notify)
1751 TnyHeader *header = NULL;
1752 TnyFolder *folder = NULL;
1754 ModestMailOperationPrivate *priv = NULL;
1755 GetFullMsgsInfo *info = NULL;
1756 gboolean size_ok = TRUE;
1758 TnyIterator *iter = NULL;
1760 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1762 /* Init mail operation */
1763 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1764 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1766 priv->total = tny_list_get_length(header_list);
1768 /* Get account and set it into mail_operation */
1769 if (tny_list_get_length (header_list) >= 1) {
1770 iter = tny_list_create_iterator (header_list);
1771 header = TNY_HEADER (tny_iterator_get_current (iter));
1772 folder = tny_header_get_folder (header);
1773 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1774 g_object_unref (header);
1775 g_object_unref (folder);
1777 if (tny_list_get_length (header_list) == 1) {
1778 g_object_unref (iter);
1783 /* Get msg size limit */
1784 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1785 MODEST_CONF_MSG_SIZE_LIMIT,
1788 g_clear_error (&(priv->error));
1789 max_size = G_MAXINT;
1791 max_size = max_size * KB;
1794 /* Check message size limits. If there is only one message
1795 always retrieve it */
1797 while (!tny_iterator_is_done (iter) && size_ok) {
1798 header = TNY_HEADER (tny_iterator_get_current (iter));
1799 if (tny_header_get_message_size (header) >= max_size)
1801 g_object_unref (header);
1802 tny_iterator_next (iter);
1804 g_object_unref (iter);
1808 /* Create the info */
1809 info = g_slice_new0 (GetFullMsgsInfo);
1810 info->mail_op = self;
1811 info->user_callback = user_callback;
1812 info->user_data = user_data;
1813 info->headers = g_object_ref (header_list);
1814 info->notify = notify;
1816 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1818 /* Set status failed and set an error */
1819 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1820 /* FIXME: the error msg is different for pop */
1821 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1822 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1823 _("emev_ni_ui_imap_msg_size_exceed_error"));
1824 /* Remove from queue and free resources */
1825 modest_mail_operation_notify_end (self);
1833 modest_mail_operation_remove_msg (ModestMailOperation *self,
1835 gboolean remove_to_trash)
1838 ModestMailOperationPrivate *priv;
1840 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1841 g_return_if_fail (TNY_IS_HEADER (header));
1843 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1844 folder = tny_header_get_folder (header);
1846 /* Get account and set it into mail_operation */
1847 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1849 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1851 /* Delete or move to trash */
1852 if (remove_to_trash) {
1853 TnyFolder *trash_folder;
1854 TnyStoreAccount *store_account;
1856 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1857 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1858 TNY_FOLDER_TYPE_TRASH);
1863 headers = tny_simple_list_new ();
1864 tny_list_append (headers, G_OBJECT (header));
1865 g_object_unref (header);
1868 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1869 g_object_unref (headers);
1870 /* g_object_unref (trash_folder); */
1872 ModestMailOperationPrivate *priv;
1874 /* Set status failed and set an error */
1875 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1876 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1877 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1878 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1879 _("Error trying to delete a message. Trash folder not found"));
1882 g_object_unref (G_OBJECT (store_account));
1884 tny_folder_remove_msg (folder, header, &(priv->error));
1886 tny_folder_sync(folder, TRUE, &(priv->error));
1891 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1893 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1896 g_object_unref (G_OBJECT (folder));
1898 /* Notify about operation end */
1899 modest_mail_operation_notify_end (self);
1903 transfer_msgs_status_cb (GObject *obj,
1907 XFerMsgAsyncHelper *helper = NULL;
1908 ModestMailOperation *self;
1909 ModestMailOperationPrivate *priv;
1910 ModestMailOperationState *state;
1913 g_return_if_fail (status != NULL);
1914 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1916 helper = (XFerMsgAsyncHelper *) user_data;
1917 g_return_if_fail (helper != NULL);
1919 self = helper->mail_op;
1920 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1922 if ((status->position == 1) && (status->of_total == 100))
1925 priv->done = status->position;
1926 priv->total = status->of_total;
1928 state = modest_mail_operation_clone_state (self);
1929 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1930 g_slice_free (ModestMailOperationState, state);
1935 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1937 XFerMsgAsyncHelper *helper;
1938 ModestMailOperation *self;
1939 ModestMailOperationPrivate *priv;
1941 helper = (XFerMsgAsyncHelper *) user_data;
1942 self = helper->mail_op;
1944 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1947 priv->error = g_error_copy (*err);
1949 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1950 } else if (cancelled) {
1951 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1952 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1953 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1954 _("Error trying to refresh the contents of %s"),
1955 tny_folder_get_name (folder));
1958 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1961 /* If user defined callback function was defined, call it */
1962 if (helper->user_callback) {
1963 helper->user_callback (priv->source, helper->user_data);
1967 g_object_unref (helper->headers);
1968 g_object_unref (helper->dest_folder);
1969 g_object_unref (helper->mail_op);
1970 g_slice_free (XFerMsgAsyncHelper, helper);
1971 g_object_unref (folder);
1973 /* Notify about operation end */
1974 modest_mail_operation_notify_end (self);
1978 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1981 gboolean delete_original,
1982 XferMsgsAsynUserCallback user_callback,
1985 ModestMailOperationPrivate *priv;
1987 TnyFolder *src_folder;
1988 XFerMsgAsyncHelper *helper;
1990 ModestTnyFolderRules rules;
1992 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1993 g_return_if_fail (TNY_IS_LIST (headers));
1994 g_return_if_fail (TNY_IS_FOLDER (folder));
1996 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1999 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2001 /* Apply folder rules */
2002 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2003 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2004 /* Set status failed and set an error */
2005 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2006 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2007 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2008 _("FIXME: folder does not accept msgs"));
2009 /* Notify the queue */
2010 modest_mail_operation_notify_end (self);
2014 /* Create the helper */
2015 helper = g_slice_new0 (XFerMsgAsyncHelper);
2016 helper->mail_op = g_object_ref(self);
2017 helper->dest_folder = g_object_ref(folder);
2018 helper->headers = g_object_ref(headers);
2019 helper->user_callback = user_callback;
2020 helper->user_data = user_data;
2022 /* Get source folder */
2023 iter = tny_list_create_iterator (headers);
2024 header = TNY_HEADER (tny_iterator_get_current (iter));
2025 src_folder = tny_header_get_folder (header);
2026 g_object_unref (header);
2027 g_object_unref (iter);
2029 /* Get account and set it into mail_operation */
2030 priv->account = modest_tny_folder_get_account (src_folder);
2032 /* Transfer messages */
2033 tny_folder_transfer_msgs_async (src_folder,
2038 transfer_msgs_status_cb,
2044 on_refresh_folder (TnyFolder *folder,
2049 RefreshAsyncHelper *helper = NULL;
2050 ModestMailOperation *self = NULL;
2051 ModestMailOperationPrivate *priv = NULL;
2053 helper = (RefreshAsyncHelper *) user_data;
2054 self = helper->mail_op;
2055 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2058 priv->error = g_error_copy (*error);
2059 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2064 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2065 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2066 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2067 _("Error trying to refresh the contents of %s"),
2068 tny_folder_get_name (folder));
2072 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2075 /* Call user defined callback, if it exists */
2076 if (helper->user_callback)
2077 helper->user_callback (priv->source, folder, helper->user_data);
2080 g_object_unref (helper->mail_op);
2081 g_slice_free (RefreshAsyncHelper, helper);
2082 g_object_unref (folder);
2084 /* Notify about operation end */
2085 modest_mail_operation_notify_end (self);
2089 on_refresh_folder_status_update (GObject *obj,
2093 RefreshAsyncHelper *helper = NULL;
2094 ModestMailOperation *self = NULL;
2095 ModestMailOperationPrivate *priv = NULL;
2096 ModestMailOperationState *state;
2098 g_return_if_fail (user_data != NULL);
2099 g_return_if_fail (status != NULL);
2100 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2102 helper = (RefreshAsyncHelper *) user_data;
2103 self = helper->mail_op;
2104 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2106 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2108 priv->done = status->position;
2109 priv->total = status->of_total;
2111 state = modest_mail_operation_clone_state (self);
2112 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2113 g_slice_free (ModestMailOperationState, state);
2117 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2119 RefreshAsyncUserCallback user_callback,
2122 ModestMailOperationPrivate *priv = NULL;
2123 RefreshAsyncHelper *helper = NULL;
2125 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2127 /* Pick a reference */
2128 g_object_ref (folder);
2130 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2132 /* Get account and set it into mail_operation */
2133 priv->account = modest_tny_folder_get_account (folder);
2135 /* Create the helper */
2136 helper = g_slice_new0 (RefreshAsyncHelper);
2137 helper->mail_op = g_object_ref(self);
2138 helper->user_callback = user_callback;
2139 helper->user_data = user_data;
2141 /* Refresh the folder. TODO: tinymail could issue a status
2142 updates before the callback call then this could happen. We
2143 must review the design */
2144 tny_folder_refresh_async (folder,
2146 on_refresh_folder_status_update,
2152 * It's used by the mail operation queue to notify the observers
2153 * attached to that signal that the operation finished. We need to use
2154 * that because tinymail does not give us the progress of a given
2155 * operation when it finishes (it directly calls the operation
2159 modest_mail_operation_notify_end (ModestMailOperation *self)
2161 ModestMailOperationState *state;
2162 ModestMailOperationPrivate *priv = NULL;
2164 g_return_if_fail (self);
2166 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2169 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2173 /* Set the account back to not busy */
2174 if (priv->account_name) {
2175 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2177 g_free(priv->account_name);
2178 priv->account_name = NULL;
2181 /* Notify the observers about the mail opertation end */
2182 state = modest_mail_operation_clone_state (self);
2183 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2184 g_slice_free (ModestMailOperationState, state);