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));
1094 if (info->callback) {
1095 /* This thread is not in the main lock */
1096 gdk_threads_enter ();
1097 info->callback (info->mail_op,
1098 (new_headers) ? new_headers->len : 0,
1100 gdk_threads_leave ();
1104 g_object_unref (query);
1105 g_object_unref (all_folders);
1106 g_object_unref (info->account);
1107 g_object_unref (info->transport_account);
1108 g_free (info->retrieve_type);
1109 g_slice_free (UpdateAccountInfo, info);
1117 modest_mail_operation_update_account (ModestMailOperation *self,
1118 const gchar *account_name,
1119 UpdateAccountCallback callback,
1123 UpdateAccountInfo *info;
1124 ModestMailOperationPrivate *priv;
1125 ModestAccountMgr *mgr;
1126 TnyStoreAccount *modest_account;
1127 TnyTransportAccount *transport_account;
1129 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1130 g_return_val_if_fail (account_name, FALSE);
1132 /* Init mail operation. Set total and done to 0, and do not
1133 update them, this way the progress objects will know that
1134 we have no clue about the number of the objects */
1135 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1138 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1140 /* Make sure that we have a connection, and request one
1142 * TODO: Is there some way to trigger this for every attempt to
1143 * use the network? */
1144 if (!modest_platform_connect_and_wait(NULL))
1147 /* Get the Modest account */
1148 modest_account = (TnyStoreAccount *)
1149 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1151 TNY_ACCOUNT_TYPE_STORE);
1153 if (!modest_account) {
1154 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1155 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1156 "cannot get tny store account for %s\n", account_name);
1161 /* Get the transport account, we can not do it in the thread
1162 due to some problems with dbus */
1163 transport_account = (TnyTransportAccount *)
1164 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1166 if (!transport_account) {
1167 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1168 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1169 "cannot get tny transport account for %s\n", account_name);
1173 /* Create the helper object */
1174 info = g_slice_new (UpdateAccountInfo);
1175 info->mail_op = self;
1176 info->account = modest_account;
1177 info->transport_account = transport_account;
1178 info->callback = callback;
1179 info->user_data = user_data;
1181 /* Get the message size limit */
1182 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1183 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1184 if (info->max_size == 0)
1185 info->max_size = G_MAXINT;
1187 info->max_size = info->max_size * KB;
1189 /* Get per-account retrieval type */
1190 mgr = modest_runtime_get_account_mgr ();
1191 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1192 MODEST_ACCOUNT_RETRIEVE, FALSE);
1194 /* Get per-account message amount retrieval limit */
1195 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1196 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1197 if (info->retrieve_limit == 0)
1198 info->retrieve_limit = G_MAXINT;
1200 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1202 /* Set account busy */
1203 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1204 priv->account_name = g_strdup(account_name);
1206 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1211 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1213 callback (self, 0, user_data);
1214 modest_mail_operation_notify_end (self);
1218 /* ******************************************************************* */
1219 /* ************************** STORE ACTIONS ************************* */
1220 /* ******************************************************************* */
1224 modest_mail_operation_create_folder (ModestMailOperation *self,
1225 TnyFolderStore *parent,
1228 ModestMailOperationPrivate *priv;
1229 TnyFolder *new_folder = NULL;
1231 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1232 g_return_val_if_fail (name, NULL);
1234 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1237 if (TNY_IS_FOLDER (parent)) {
1238 /* Check folder rules */
1239 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1240 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1241 /* Set status failed and set an error */
1242 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1243 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1244 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1245 _("mail_in_ui_folder_create_error"));
1250 /* Create the folder */
1251 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1252 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1254 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1257 /* Notify about operation end */
1258 modest_mail_operation_notify_end (self);
1264 modest_mail_operation_remove_folder (ModestMailOperation *self,
1266 gboolean remove_to_trash)
1268 TnyAccount *account;
1269 ModestMailOperationPrivate *priv;
1270 ModestTnyFolderRules rules;
1272 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1273 g_return_if_fail (TNY_IS_FOLDER (folder));
1275 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1277 /* Check folder rules */
1278 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1279 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1280 /* Set status failed and set an error */
1281 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1282 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1283 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1284 _("mail_in_ui_folder_delete_error"));
1288 /* Get the account */
1289 account = modest_tny_folder_get_account (folder);
1290 priv->account = g_object_ref(account);
1292 /* Delete folder or move to trash */
1293 if (remove_to_trash) {
1294 TnyFolder *trash_folder = NULL;
1295 trash_folder = modest_tny_account_get_special_folder (account,
1296 TNY_FOLDER_TYPE_TRASH);
1297 /* TODO: error_handling */
1298 modest_mail_operation_xfer_folder (self, folder,
1299 TNY_FOLDER_STORE (trash_folder), TRUE);
1301 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1303 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1304 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1307 g_object_unref (G_OBJECT (parent));
1309 g_object_unref (G_OBJECT (account));
1312 /* Notify about operation end */
1313 modest_mail_operation_notify_end (self);
1317 transfer_folder_status_cb (GObject *obj,
1321 ModestMailOperation *self;
1322 ModestMailOperationPrivate *priv;
1323 ModestMailOperationState *state;
1325 g_return_if_fail (status != NULL);
1326 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1328 self = MODEST_MAIL_OPERATION (user_data);
1329 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1331 if ((status->position == 1) && (status->of_total == 100))
1334 priv->done = status->position;
1335 priv->total = status->of_total;
1337 state = modest_mail_operation_clone_state (self);
1338 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1339 g_slice_free (ModestMailOperationState, state);
1344 transfer_folder_cb (TnyFolder *folder,
1345 TnyFolderStore *into,
1347 TnyFolder *new_folder, GError **err,
1350 ModestMailOperation *self = NULL;
1351 ModestMailOperationPrivate *priv = NULL;
1353 self = MODEST_MAIL_OPERATION (user_data);
1355 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1358 priv->error = g_error_copy (*err);
1360 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1361 } else if (cancelled) {
1362 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1363 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1364 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1365 _("Transference of %s was cancelled."),
1366 tny_folder_get_name (folder));
1369 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1373 g_object_unref (folder);
1374 g_object_unref (into);
1375 if (new_folder != NULL)
1376 g_object_unref (new_folder);
1378 /* Notify about operation end */
1379 modest_mail_operation_notify_end (self);
1383 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1385 TnyFolderStore *parent,
1386 gboolean delete_original)
1388 ModestMailOperationPrivate *priv = NULL;
1389 ModestTnyFolderRules parent_rules, rules;
1391 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1392 g_return_if_fail (TNY_IS_FOLDER (folder));
1393 g_return_if_fail (TNY_IS_FOLDER (parent));
1395 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1397 /* Get account and set it into mail_operation */
1398 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1399 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1401 /* Get folder rules */
1402 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1403 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1405 if (!TNY_IS_FOLDER_STORE (parent)) {
1409 /* The moveable restriction is applied also to copy operation */
1410 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1411 /* Set status failed and set an error */
1412 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1413 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1414 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1415 _("mail_in_ui_folder_move_target_error"));
1417 /* Notify the queue */
1418 modest_mail_operation_notify_end (self);
1419 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1420 /* Set status failed and set an error */
1421 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1422 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1423 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1424 _("FIXME: parent folder does not accept new folders"));
1426 /* Notify the queue */
1427 modest_mail_operation_notify_end (self);
1429 /* Pick references for async calls */
1430 g_object_ref (folder);
1431 g_object_ref (parent);
1433 /* Move/Copy folder */
1434 tny_folder_copy_async (folder,
1436 tny_folder_get_name (folder),
1439 transfer_folder_status_cb,
1445 modest_mail_operation_rename_folder (ModestMailOperation *self,
1449 ModestMailOperationPrivate *priv;
1450 ModestTnyFolderRules rules;
1452 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1453 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1454 g_return_if_fail (name);
1456 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1458 /* Get account and set it into mail_operation */
1459 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1461 /* Check folder rules */
1462 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1463 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1464 /* Set status failed and set an error */
1465 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1466 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1467 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1468 _("FIXME: unable to rename"));
1470 /* Notify about operation end */
1471 modest_mail_operation_notify_end (self);
1473 /* Rename. Camel handles folder subscription/unsubscription */
1474 TnyFolderStore *into;
1476 into = tny_folder_get_folder_store (folder);
1477 tny_folder_copy_async (folder, into, name, TRUE,
1479 transfer_folder_status_cb,
1482 g_object_unref (into);
1487 /* ******************************************************************* */
1488 /* ************************** MSG ACTIONS ************************* */
1489 /* ******************************************************************* */
1491 void modest_mail_operation_get_msg (ModestMailOperation *self,
1493 GetMsgAsyncUserCallback user_callback,
1496 GetMsgAsyncHelper *helper = NULL;
1498 ModestMailOperationPrivate *priv;
1500 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1501 g_return_if_fail (TNY_IS_HEADER (header));
1503 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1504 folder = tny_header_get_folder (header);
1506 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1508 /* Get message from folder */
1510 /* Get account and set it into mail_operation */
1511 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1513 helper = g_slice_new0 (GetMsgAsyncHelper);
1514 helper->mail_op = self;
1515 helper->user_callback = user_callback;
1516 helper->user_data = user_data;
1517 helper->header = g_object_ref (header);
1519 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1521 g_object_unref (G_OBJECT (folder));
1523 /* Set status failed and set an error */
1524 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1525 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1526 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1527 _("Error trying to get a message. No folder found for header"));
1529 /* Notify the queue */
1530 modest_mail_operation_notify_end (self);
1535 get_msg_cb (TnyFolder *folder,
1541 GetMsgAsyncHelper *helper = NULL;
1542 ModestMailOperation *self = NULL;
1543 ModestMailOperationPrivate *priv = NULL;
1545 helper = (GetMsgAsyncHelper *) user_data;
1546 g_return_if_fail (helper != NULL);
1547 self = helper->mail_op;
1548 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1549 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1551 /* Check errors and cancel */
1553 priv->error = g_error_copy (*error);
1554 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1558 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1559 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1560 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1561 _("Error trying to refresh the contents of %s"),
1562 tny_folder_get_name (folder));
1566 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1568 /* If user defined callback function was defined, call it */
1569 if (helper->user_callback) {
1570 /* This callback is called into an iddle by tinymail,
1571 and idles are not in the main lock */
1572 gdk_threads_enter ();
1573 helper->user_callback (self, helper->header, msg, helper->user_data);
1574 gdk_threads_leave ();
1579 g_object_unref (helper->header);
1580 g_slice_free (GetMsgAsyncHelper, helper);
1582 /* Notify about operation end */
1583 modest_mail_operation_notify_end (self);
1587 get_msg_status_cb (GObject *obj,
1591 GetMsgAsyncHelper *helper = NULL;
1592 ModestMailOperation *self;
1593 ModestMailOperationPrivate *priv;
1594 ModestMailOperationState *state;
1596 g_return_if_fail (status != NULL);
1597 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1599 helper = (GetMsgAsyncHelper *) user_data;
1600 g_return_if_fail (helper != NULL);
1602 self = helper->mail_op;
1603 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1605 if ((status->position == 1) && (status->of_total == 100))
1611 state = modest_mail_operation_clone_state (self);
1612 state->bytes_done = status->position;
1613 state->bytes_total = status->of_total;
1614 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1615 g_slice_free (ModestMailOperationState, state);
1618 /****************************************************/
1620 ModestMailOperation *mail_op;
1622 GetMsgAsyncUserCallback user_callback;
1624 GDestroyNotify notify;
1628 GetMsgAsyncUserCallback user_callback;
1632 ModestMailOperation *mail_op;
1633 } NotifyGetMsgsInfo;
1637 * Used by get_msgs_full_thread to call the user_callback for each
1638 * message that has been read
1641 notify_get_msgs_full (gpointer data)
1643 NotifyGetMsgsInfo *info;
1645 info = (NotifyGetMsgsInfo *) data;
1647 /* Call the user callback. Idles are not in the main lock, so
1649 gdk_threads_enter ();
1650 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1651 gdk_threads_leave ();
1653 g_slice_free (NotifyGetMsgsInfo, info);
1659 * Used by get_msgs_full_thread to free al the thread resources and to
1660 * call the destroy function for the passed user_data
1663 get_msgs_full_destroyer (gpointer data)
1665 GetFullMsgsInfo *info;
1667 info = (GetFullMsgsInfo *) data;
1670 info->notify (info->user_data);
1673 g_object_unref (info->headers);
1674 g_slice_free (GetFullMsgsInfo, info);
1680 get_msgs_full_thread (gpointer thr_user_data)
1682 GetFullMsgsInfo *info;
1683 ModestMailOperationPrivate *priv = NULL;
1684 TnyIterator *iter = NULL;
1686 info = (GetFullMsgsInfo *) thr_user_data;
1687 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1689 iter = tny_list_create_iterator (info->headers);
1690 while (!tny_iterator_is_done (iter)) {
1694 header = TNY_HEADER (tny_iterator_get_current (iter));
1695 folder = tny_header_get_folder (header);
1697 /* Get message from folder */
1700 /* The callback will call it per each header */
1701 msg = tny_folder_get_msg (folder, header, &(priv->error));
1704 ModestMailOperationState *state;
1709 /* notify progress */
1710 state = modest_mail_operation_clone_state (info->mail_op);
1711 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1712 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1713 pair, (GDestroyNotify) modest_pair_free);
1715 /* The callback is the responsible for
1716 freeing the message */
1717 if (info->user_callback) {
1718 NotifyGetMsgsInfo *info_notify;
1719 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1720 info_notify->user_callback = info->user_callback;
1721 info_notify->mail_op = info->mail_op;
1722 info_notify->header = g_object_ref (header);
1723 info_notify->msg = g_object_ref (msg);
1724 info_notify->user_data = info->user_data;
1725 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1726 notify_get_msgs_full,
1729 g_object_unref (msg);
1732 /* Set status failed and set an error */
1733 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1734 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1735 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1736 "Error trying to get a message. No folder found for header");
1738 g_object_unref (header);
1739 tny_iterator_next (iter);
1742 /* Set operation status */
1743 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1744 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1746 /* Notify about operation end */
1747 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1749 /* Free thread resources. Will be called after all previous idles */
1750 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1756 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1757 TnyList *header_list,
1758 GetMsgAsyncUserCallback user_callback,
1760 GDestroyNotify notify)
1762 TnyHeader *header = NULL;
1763 TnyFolder *folder = NULL;
1765 ModestMailOperationPrivate *priv = NULL;
1766 GetFullMsgsInfo *info = NULL;
1767 gboolean size_ok = TRUE;
1769 TnyIterator *iter = NULL;
1771 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1773 /* Init mail operation */
1774 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1775 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1777 priv->total = tny_list_get_length(header_list);
1779 /* Get account and set it into mail_operation */
1780 if (tny_list_get_length (header_list) >= 1) {
1781 iter = tny_list_create_iterator (header_list);
1782 header = TNY_HEADER (tny_iterator_get_current (iter));
1783 folder = tny_header_get_folder (header);
1784 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1785 g_object_unref (header);
1786 g_object_unref (folder);
1788 if (tny_list_get_length (header_list) == 1) {
1789 g_object_unref (iter);
1794 /* Get msg size limit */
1795 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1796 MODEST_CONF_MSG_SIZE_LIMIT,
1799 g_clear_error (&(priv->error));
1800 max_size = G_MAXINT;
1802 max_size = max_size * KB;
1805 /* Check message size limits. If there is only one message
1806 always retrieve it */
1808 while (!tny_iterator_is_done (iter) && size_ok) {
1809 header = TNY_HEADER (tny_iterator_get_current (iter));
1810 if (tny_header_get_message_size (header) >= max_size)
1812 g_object_unref (header);
1813 tny_iterator_next (iter);
1815 g_object_unref (iter);
1819 /* Create the info */
1820 info = g_slice_new0 (GetFullMsgsInfo);
1821 info->mail_op = self;
1822 info->user_callback = user_callback;
1823 info->user_data = user_data;
1824 info->headers = g_object_ref (header_list);
1825 info->notify = notify;
1827 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1829 /* Set status failed and set an error */
1830 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1831 /* FIXME: the error msg is different for pop */
1832 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1833 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1834 _("emev_ni_ui_imap_msg_size_exceed_error"));
1835 /* Remove from queue and free resources */
1836 modest_mail_operation_notify_end (self);
1844 modest_mail_operation_remove_msg (ModestMailOperation *self,
1846 gboolean remove_to_trash)
1849 ModestMailOperationPrivate *priv;
1851 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1852 g_return_if_fail (TNY_IS_HEADER (header));
1854 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1855 folder = tny_header_get_folder (header);
1857 /* Get account and set it into mail_operation */
1858 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1860 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1862 /* Delete or move to trash */
1863 if (remove_to_trash) {
1864 TnyFolder *trash_folder;
1865 TnyStoreAccount *store_account;
1867 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1868 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1869 TNY_FOLDER_TYPE_TRASH);
1874 headers = tny_simple_list_new ();
1875 tny_list_append (headers, G_OBJECT (header));
1876 g_object_unref (header);
1879 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1880 g_object_unref (headers);
1881 /* g_object_unref (trash_folder); */
1883 ModestMailOperationPrivate *priv;
1885 /* Set status failed and set an error */
1886 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1887 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1888 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1889 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1890 _("Error trying to delete a message. Trash folder not found"));
1893 g_object_unref (G_OBJECT (store_account));
1895 tny_folder_remove_msg (folder, header, &(priv->error));
1897 tny_folder_sync(folder, TRUE, &(priv->error));
1902 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1904 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1907 g_object_unref (G_OBJECT (folder));
1909 /* Notify about operation end */
1910 modest_mail_operation_notify_end (self);
1914 transfer_msgs_status_cb (GObject *obj,
1918 XFerMsgAsyncHelper *helper = NULL;
1919 ModestMailOperation *self;
1920 ModestMailOperationPrivate *priv;
1921 ModestMailOperationState *state;
1924 g_return_if_fail (status != NULL);
1925 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1927 helper = (XFerMsgAsyncHelper *) user_data;
1928 g_return_if_fail (helper != NULL);
1930 self = helper->mail_op;
1931 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1933 if ((status->position == 1) && (status->of_total == 100))
1936 priv->done = status->position;
1937 priv->total = status->of_total;
1939 state = modest_mail_operation_clone_state (self);
1940 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1941 g_slice_free (ModestMailOperationState, state);
1946 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1948 XFerMsgAsyncHelper *helper;
1949 ModestMailOperation *self;
1950 ModestMailOperationPrivate *priv;
1952 helper = (XFerMsgAsyncHelper *) user_data;
1953 self = helper->mail_op;
1955 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1958 priv->error = g_error_copy (*err);
1960 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1961 } else if (cancelled) {
1962 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1963 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1964 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1965 _("Error trying to refresh the contents of %s"),
1966 tny_folder_get_name (folder));
1969 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1972 /* If user defined callback function was defined, call it */
1973 if (helper->user_callback) {
1974 helper->user_callback (priv->source, helper->user_data);
1978 g_object_unref (helper->headers);
1979 g_object_unref (helper->dest_folder);
1980 g_object_unref (helper->mail_op);
1981 g_slice_free (XFerMsgAsyncHelper, helper);
1982 g_object_unref (folder);
1984 /* Notify about operation end */
1985 modest_mail_operation_notify_end (self);
1989 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1992 gboolean delete_original,
1993 XferMsgsAsynUserCallback user_callback,
1996 ModestMailOperationPrivate *priv;
1998 TnyFolder *src_folder;
1999 XFerMsgAsyncHelper *helper;
2001 ModestTnyFolderRules rules;
2003 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2004 g_return_if_fail (TNY_IS_LIST (headers));
2005 g_return_if_fail (TNY_IS_FOLDER (folder));
2007 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2010 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2012 /* Apply folder rules */
2013 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2014 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2015 /* Set status failed and set an error */
2016 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2017 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2018 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2019 _("FIXME: folder does not accept msgs"));
2020 /* Notify the queue */
2021 modest_mail_operation_notify_end (self);
2025 /* Create the helper */
2026 helper = g_slice_new0 (XFerMsgAsyncHelper);
2027 helper->mail_op = g_object_ref(self);
2028 helper->dest_folder = g_object_ref(folder);
2029 helper->headers = g_object_ref(headers);
2030 helper->user_callback = user_callback;
2031 helper->user_data = user_data;
2033 /* Get source folder */
2034 iter = tny_list_create_iterator (headers);
2035 header = TNY_HEADER (tny_iterator_get_current (iter));
2036 src_folder = tny_header_get_folder (header);
2037 g_object_unref (header);
2038 g_object_unref (iter);
2040 /* Get account and set it into mail_operation */
2041 priv->account = modest_tny_folder_get_account (src_folder);
2043 /* Transfer messages */
2044 tny_folder_transfer_msgs_async (src_folder,
2049 transfer_msgs_status_cb,
2055 on_refresh_folder (TnyFolder *folder,
2060 RefreshAsyncHelper *helper = NULL;
2061 ModestMailOperation *self = NULL;
2062 ModestMailOperationPrivate *priv = NULL;
2064 helper = (RefreshAsyncHelper *) user_data;
2065 self = helper->mail_op;
2066 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2069 priv->error = g_error_copy (*error);
2070 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2075 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2076 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2077 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2078 _("Error trying to refresh the contents of %s"),
2079 tny_folder_get_name (folder));
2083 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2086 /* Call user defined callback, if it exists */
2087 if (helper->user_callback)
2088 helper->user_callback (priv->source, folder, helper->user_data);
2091 g_object_unref (helper->mail_op);
2092 g_slice_free (RefreshAsyncHelper, helper);
2093 g_object_unref (folder);
2095 /* Notify about operation end */
2096 modest_mail_operation_notify_end (self);
2100 on_refresh_folder_status_update (GObject *obj,
2104 RefreshAsyncHelper *helper = NULL;
2105 ModestMailOperation *self = NULL;
2106 ModestMailOperationPrivate *priv = NULL;
2107 ModestMailOperationState *state;
2109 g_return_if_fail (user_data != NULL);
2110 g_return_if_fail (status != NULL);
2111 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2113 helper = (RefreshAsyncHelper *) user_data;
2114 self = helper->mail_op;
2115 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2117 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2119 priv->done = status->position;
2120 priv->total = status->of_total;
2122 state = modest_mail_operation_clone_state (self);
2123 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2124 g_slice_free (ModestMailOperationState, state);
2128 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2130 RefreshAsyncUserCallback user_callback,
2133 ModestMailOperationPrivate *priv = NULL;
2134 RefreshAsyncHelper *helper = NULL;
2136 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2138 /* Pick a reference */
2139 g_object_ref (folder);
2141 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2143 /* Get account and set it into mail_operation */
2144 priv->account = modest_tny_folder_get_account (folder);
2146 /* Create the helper */
2147 helper = g_slice_new0 (RefreshAsyncHelper);
2148 helper->mail_op = g_object_ref(self);
2149 helper->user_callback = user_callback;
2150 helper->user_data = user_data;
2152 /* Refresh the folder. TODO: tinymail could issue a status
2153 updates before the callback call then this could happen. We
2154 must review the design */
2155 tny_folder_refresh_async (folder,
2157 on_refresh_folder_status_update,
2163 * It's used by the mail operation queue to notify the observers
2164 * attached to that signal that the operation finished. We need to use
2165 * that because tinymail does not give us the progress of a given
2166 * operation when it finishes (it directly calls the operation
2170 modest_mail_operation_notify_end (ModestMailOperation *self)
2172 ModestMailOperationState *state;
2173 ModestMailOperationPrivate *priv = NULL;
2175 g_return_if_fail (self);
2177 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2180 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2184 /* Set the account back to not busy */
2185 if (priv->account_name) {
2186 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2188 g_free(priv->account_name);
2189 priv->account_name = NULL;
2192 /* Notify the observers about the mail opertation end */
2193 state = modest_mail_operation_clone_state (self);
2194 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2195 g_slice_free (ModestMailOperationState, state);