1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize (GObject *obj);
63 static void get_msg_cb (TnyFolder *folder,
69 static void get_msg_status_cb (GObject *obj,
73 static void modest_mail_operation_notify_end (ModestMailOperation *self,
76 static gboolean did_a_cancel = FALSE;
78 enum _ModestMailOperationSignals
80 PROGRESS_CHANGED_SIGNAL,
85 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
86 struct _ModestMailOperationPrivate {
93 ErrorCheckingUserCallback error_checking;
94 gpointer error_checking_user_data;
95 ModestMailOperationStatus status;
96 ModestMailOperationTypeOperation op_type;
99 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
100 MODEST_TYPE_MAIL_OPERATION, \
101 ModestMailOperationPrivate))
103 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
104 priv->status = new_status;\
107 typedef struct _GetMsgAsyncHelper {
108 ModestMailOperation *mail_op;
110 GetMsgAsyncUserCallback user_callback;
114 typedef struct _RefreshAsyncHelper {
115 ModestMailOperation *mail_op;
116 RefreshAsyncUserCallback user_callback;
118 } RefreshAsyncHelper;
120 typedef struct _XFerMsgAsyncHelper
122 ModestMailOperation *mail_op;
124 TnyFolder *dest_folder;
125 XferMsgsAsynUserCallback user_callback;
127 } XFerMsgAsyncHelper;
130 static GObjectClass *parent_class = NULL;
132 static guint signals[NUM_SIGNALS] = {0};
135 modest_mail_operation_get_type (void)
137 static GType my_type = 0;
139 static const GTypeInfo my_info = {
140 sizeof(ModestMailOperationClass),
141 NULL, /* base init */
142 NULL, /* base finalize */
143 (GClassInitFunc) modest_mail_operation_class_init,
144 NULL, /* class finalize */
145 NULL, /* class data */
146 sizeof(ModestMailOperation),
148 (GInstanceInitFunc) modest_mail_operation_init,
151 my_type = g_type_register_static (G_TYPE_OBJECT,
152 "ModestMailOperation",
159 modest_mail_operation_class_init (ModestMailOperationClass *klass)
161 GObjectClass *gobject_class;
162 gobject_class = (GObjectClass*) klass;
164 parent_class = g_type_class_peek_parent (klass);
165 gobject_class->finalize = modest_mail_operation_finalize;
167 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
170 * ModestMailOperation::progress-changed
171 * @self: the #MailOperation that emits the signal
172 * @user_data: user data set when the signal handler was connected
174 * Emitted when the progress of a mail operation changes
176 signals[PROGRESS_CHANGED_SIGNAL] =
177 g_signal_new ("progress-changed",
178 G_TYPE_FROM_CLASS (gobject_class),
180 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
182 g_cclosure_marshal_VOID__POINTER,
183 G_TYPE_NONE, 1, G_TYPE_POINTER);
188 modest_mail_operation_init (ModestMailOperation *obj)
190 ModestMailOperationPrivate *priv;
192 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
194 priv->account = NULL;
195 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
196 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
201 priv->error_checking = NULL;
202 priv->error_checking_user_data = NULL;
206 modest_mail_operation_finalize (GObject *obj)
208 ModestMailOperationPrivate *priv;
210 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
215 g_error_free (priv->error);
219 g_object_unref (priv->source);
223 g_object_unref (priv->account);
224 priv->account = NULL;
228 G_OBJECT_CLASS(parent_class)->finalize (obj);
232 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
235 ModestMailOperation *obj;
236 ModestMailOperationPrivate *priv;
238 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
239 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
241 priv->op_type = op_type;
243 priv->source = g_object_ref(source);
249 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
251 ErrorCheckingUserCallback error_handler,
254 ModestMailOperation *obj;
255 ModestMailOperationPrivate *priv;
257 obj = modest_mail_operation_new (op_type, source);
258 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
260 g_return_val_if_fail (error_handler != NULL, obj);
261 priv->error_checking = error_handler;
267 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
269 ModestMailOperationPrivate *priv;
271 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
272 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
274 if (priv->error_checking != NULL)
275 priv->error_checking (self, priv->error_checking_user_data);
279 ModestMailOperationTypeOperation
280 modest_mail_operation_get_type_operation (ModestMailOperation *self)
282 ModestMailOperationPrivate *priv;
284 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
286 return priv->op_type;
290 modest_mail_operation_is_mine (ModestMailOperation *self,
293 ModestMailOperationPrivate *priv;
295 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
296 if (priv->source == NULL) return FALSE;
298 return priv->source == me;
302 modest_mail_operation_get_source (ModestMailOperation *self)
304 ModestMailOperationPrivate *priv;
306 g_return_val_if_fail (self, NULL);
308 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
310 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
314 return g_object_ref (priv->source);
317 ModestMailOperationStatus
318 modest_mail_operation_get_status (ModestMailOperation *self)
320 ModestMailOperationPrivate *priv;
322 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
323 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
324 MODEST_MAIL_OPERATION_STATUS_INVALID);
326 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
328 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
329 return MODEST_MAIL_OPERATION_STATUS_INVALID;
336 modest_mail_operation_get_error (ModestMailOperation *self)
338 ModestMailOperationPrivate *priv;
340 g_return_val_if_fail (self, NULL);
341 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
343 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
346 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
354 modest_mail_operation_cancel (ModestMailOperation *self)
356 ModestMailOperationPrivate *priv;
358 if (!MODEST_IS_MAIL_OPERATION (self)) {
359 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
363 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
365 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
372 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
374 /* This emits progress-changed on which the mail operation queue is
375 * listening, so the mail operation is correctly removed from the
376 * queue without further explicit calls. */
377 modest_mail_operation_notify_end (self, FALSE);
383 modest_mail_operation_get_task_done (ModestMailOperation *self)
385 ModestMailOperationPrivate *priv;
387 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
389 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
394 modest_mail_operation_get_task_total (ModestMailOperation *self)
396 ModestMailOperationPrivate *priv;
398 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
400 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
405 modest_mail_operation_is_finished (ModestMailOperation *self)
407 ModestMailOperationPrivate *priv;
408 gboolean retval = FALSE;
410 if (!MODEST_IS_MAIL_OPERATION (self)) {
411 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
415 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
417 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
418 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
420 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
430 modest_mail_operation_get_id (ModestMailOperation *self)
432 ModestMailOperationPrivate *priv;
434 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
436 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
441 modest_mail_operation_set_id (ModestMailOperation *self,
444 ModestMailOperationPrivate *priv;
446 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
448 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
453 * Creates an image of the current state of a mail operation, the
454 * caller must free it
456 static ModestMailOperationState *
457 modest_mail_operation_clone_state (ModestMailOperation *self)
459 ModestMailOperationState *state;
460 ModestMailOperationPrivate *priv;
462 /* FIXME: this should be fixed properly
464 * in some cases, priv was NULL, so checking here to
467 g_return_val_if_fail (self, NULL);
468 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
469 g_return_val_if_fail (priv, NULL);
474 state = g_slice_new (ModestMailOperationState);
476 state->status = priv->status;
477 state->op_type = priv->op_type;
478 state->done = priv->done;
479 state->total = priv->total;
480 state->finished = modest_mail_operation_is_finished (self);
481 state->bytes_done = 0;
482 state->bytes_total = 0;
487 /* ******************************************************************* */
488 /* ************************** SEND ACTIONS ************************* */
489 /* ******************************************************************* */
492 modest_mail_operation_send_mail (ModestMailOperation *self,
493 TnyTransportAccount *transport_account,
496 TnySendQueue *send_queue = NULL;
497 ModestMailOperationPrivate *priv;
499 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
500 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
501 g_return_if_fail (TNY_IS_MSG (msg));
503 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
505 /* Get account and set it into mail_operation */
506 priv->account = g_object_ref (transport_account);
510 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
511 if (!TNY_IS_SEND_QUEUE(send_queue)) {
512 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
513 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
514 "modest: could not find send queue for account\n");
516 /* TODO: connect to the msg-sent in order to know when
517 the mail operation is finished */
518 tny_send_queue_add (send_queue, msg, &(priv->error));
519 /* TODO: we're setting always success, do the check in
521 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
524 /* TODO: do this in the handler of the "msg-sent"
525 signal.Notify about operation end */
526 modest_mail_operation_notify_end (self, FALSE);
530 modest_mail_operation_send_new_mail (ModestMailOperation *self,
531 TnyTransportAccount *transport_account,
533 const gchar *from, const gchar *to,
534 const gchar *cc, const gchar *bcc,
535 const gchar *subject, const gchar *plain_body,
536 const gchar *html_body,
537 const GList *attachments_list,
538 TnyHeaderFlags priority_flags)
540 TnyMsg *new_msg = NULL;
541 TnyFolder *folder = NULL;
542 TnyHeader *header = NULL;
543 ModestMailOperationPrivate *priv = NULL;
545 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
546 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
548 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
550 /* Check parametters */
552 /* Set status failed and set an error */
553 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
554 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
555 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
556 _("Error trying to send a mail. You need to set at least one recipient"));
560 if (html_body == NULL) {
561 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
563 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
566 g_printerr ("modest: failed to create a new msg\n");
570 /* Set priority flags in message */
571 header = tny_msg_get_header (new_msg);
572 if (priority_flags != 0)
573 tny_header_set_flags (header, priority_flags);
575 /* Call mail operation */
576 modest_mail_operation_send_mail (self, transport_account, new_msg);
578 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
580 if (draft_msg != NULL) {
581 header = tny_msg_get_header (draft_msg);
582 /* Note: This can fail (with a warning) if the message is not really already in a folder,
583 * because this function requires it to have a UID. */
584 tny_folder_remove_msg (folder, header, NULL);
585 g_object_unref (header);
590 g_object_unref (G_OBJECT (new_msg));
594 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
595 TnyTransportAccount *transport_account,
597 const gchar *from, const gchar *to,
598 const gchar *cc, const gchar *bcc,
599 const gchar *subject, const gchar *plain_body,
600 const gchar *html_body,
601 const GList *attachments_list,
602 TnyHeaderFlags priority_flags)
605 TnyFolder *folder = NULL;
606 TnyHeader *header = NULL;
607 ModestMailOperationPrivate *priv = NULL;
609 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
610 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
612 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
614 /* Get account and set it into mail_operation */
615 priv->account = g_object_ref (transport_account);
617 if (html_body == NULL) {
618 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
620 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
623 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
624 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
625 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
626 "modest: failed to create a new msg\n");
630 /* add priority flags */
631 header = tny_msg_get_header (msg);
632 tny_header_set_flags (header, priority_flags);
634 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
636 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
637 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
638 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
639 "modest: failed to create a new msg\n");
643 if (draft_msg != NULL) {
644 header = tny_msg_get_header (draft_msg);
645 /* Remove the old draft expunging it */
646 tny_folder_remove_msg (folder, header, NULL);
647 tny_folder_sync (folder, TRUE, NULL);
648 g_object_unref (header);
651 tny_folder_add_msg (folder, msg, &(priv->error));
653 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
657 g_object_unref (G_OBJECT(msg));
659 g_object_unref (G_OBJECT(folder));
661 modest_mail_operation_notify_end (self, FALSE);
666 ModestMailOperation *mail_op;
667 TnyStoreAccount *account;
668 TnyTransportAccount *transport_account;
671 gchar *retrieve_type;
673 UpdateAccountCallback callback;
677 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
678 /* We use this folder observer to track the headers that have been
679 * added to a folder */
682 TnyList *new_headers;
683 } InternalFolderObserver;
687 } InternalFolderObserverClass;
689 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
691 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
692 internal_folder_observer,
694 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
698 foreach_add_item (gpointer header, gpointer user_data)
700 /* printf("DEBUG: %s: header subject=%s\n",
701 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
703 tny_list_prepend (TNY_LIST (user_data),
704 g_object_ref (G_OBJECT (header)));
707 /* This is the method that looks for new messages in a folder */
709 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
711 InternalFolderObserver *derived = (InternalFolderObserver *)self;
713 TnyFolderChangeChanged changed;
715 changed = tny_folder_change_get_changed (change);
717 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
720 /* Get added headers */
721 list = tny_simple_list_new ();
722 tny_folder_change_get_added_headers (change, list);
724 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
725 * __FUNCTION__, tny_list_get_length(list));
728 /* Add them to the folder observer */
729 tny_list_foreach (list, foreach_add_item,
730 derived->new_headers);
732 g_object_unref (G_OBJECT (list));
737 internal_folder_observer_init (InternalFolderObserver *self)
739 self->new_headers = tny_simple_list_new ();
742 internal_folder_observer_finalize (GObject *object)
744 InternalFolderObserver *self;
746 self = (InternalFolderObserver *) object;
747 g_object_unref (self->new_headers);
749 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
752 tny_folder_observer_init (TnyFolderObserverIface *iface)
754 iface->update_func = internal_folder_observer_update;
757 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
759 GObjectClass *object_class;
761 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
762 object_class = (GObjectClass*) klass;
763 object_class->finalize = internal_folder_observer_finalize;
769 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
772 TnyList *folders = tny_simple_list_new ();
774 tny_folder_store_get_folders (store, folders, query, NULL);
775 iter = tny_list_create_iterator (folders);
777 while (!tny_iterator_is_done (iter)) {
779 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
781 tny_list_prepend (all_folders, G_OBJECT (folder));
782 recurse_folders (folder, query, all_folders);
783 g_object_unref (G_OBJECT (folder));
785 tny_iterator_next (iter);
787 g_object_unref (G_OBJECT (iter));
788 g_object_unref (G_OBJECT (folders));
792 * Issues the "progress-changed" signal. The timer won't be removed,
793 * so you must call g_source_remove to stop the signal emission
796 idle_notify_progress (gpointer data)
798 gdk_threads_enter ();
800 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
801 ModestMailOperationState *state;
803 state = modest_mail_operation_clone_state (mail_op);
804 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
805 g_slice_free (ModestMailOperationState, state);
807 gdk_threads_leave ();
813 * Issues the "progress-changed" signal and removes the timer. It uses
814 * a lock to ensure that the progress information of the mail
815 * operation is not modified while there are notifications pending
818 idle_notify_progress_once (gpointer data)
820 gdk_threads_enter ();
824 pair = (ModestPair *) data;
826 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
828 /* Free the state and the reference to the mail operation */
829 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
830 g_object_unref (pair->first);
832 gdk_threads_leave ();
838 * Used by update_account_thread to notify the queue from the main
839 * loop. We call it inside an idle call to achieve that
842 idle_notify_update_account_queue (gpointer data)
844 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
845 ModestMailOperationPrivate *priv = NULL;
847 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
849 /* Do not need to block, the notify end will do it for us */
850 modest_mail_operation_notify_end (mail_op, TRUE);
851 g_object_unref (mail_op);
857 compare_headers_by_date (gconstpointer a,
860 TnyHeader **header1, **header2;
863 header1 = (TnyHeader **) a;
864 header2 = (TnyHeader **) b;
866 sent1 = tny_header_get_date_sent (*header1);
867 sent2 = tny_header_get_date_sent (*header2);
869 /* We want the most recent ones (greater time_t) at the
878 set_last_updated_idle (gpointer data)
880 gdk_threads_enter ();
882 /* It does not matter if the time is not exactly the same than
883 the time when this idle was called, it's just an
884 approximation and it won't be very different */
885 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
887 MODEST_ACCOUNT_LAST_UPDATED,
891 gdk_threads_leave ();
897 update_account_thread (gpointer thr_user_data)
899 static gboolean first_time = TRUE;
900 UpdateAccountInfo *info;
901 TnyList *all_folders = NULL;
902 GPtrArray *new_headers = NULL;
903 TnyIterator *iter = NULL;
904 TnyFolderStoreQuery *query = NULL;
905 ModestMailOperationPrivate *priv = NULL;
906 ModestTnySendQueue *send_queue = NULL;
908 info = (UpdateAccountInfo *) thr_user_data;
909 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
911 /* Get account and set it into mail_operation */
912 priv->account = g_object_ref (info->account);
915 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
916 * show any updates unless we do that
918 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
919 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
921 /* Get all the folders. We can do it synchronously because
922 we're already running in a different thread than the UI */
923 all_folders = tny_simple_list_new ();
924 query = tny_folder_store_query_new ();
925 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
926 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
931 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
935 iter = tny_list_create_iterator (all_folders);
936 while (!tny_iterator_is_done (iter)) {
937 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
939 recurse_folders (folder, query, all_folders);
940 tny_iterator_next (iter);
942 g_object_unref (G_OBJECT (iter));
944 /* Update status and notify. We need to call the notification
945 with a source function in order to call it from the main
946 loop. We need that in order not to get into trouble with
947 Gtk+. We use a timeout in order to provide more status
948 information, because the sync tinymail call does not
949 provide it for the moment */
950 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
952 /* Refresh folders */
953 new_headers = g_ptr_array_new ();
954 iter = tny_list_create_iterator (all_folders);
956 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
958 InternalFolderObserver *observer;
959 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
961 /* Refresh the folder */
962 /* Our observer receives notification of new emails during folder refreshes,
963 * so we can use observer->new_headers.
965 observer = g_object_new (internal_folder_observer_get_type (), NULL);
966 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
968 /* This gets the status information (headers) from the server.
969 * We use the blocking version, because we are already in a separate
973 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
974 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
977 /* If the retrieve type is full messages, refresh and get the messages */
978 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
980 iter = tny_list_create_iterator (observer->new_headers);
981 while (!tny_iterator_is_done (iter)) {
982 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
984 /* Apply per-message size limits */
985 if (tny_header_get_message_size (header) < info->max_size)
986 g_ptr_array_add (new_headers, g_object_ref (header));
988 g_object_unref (header);
989 tny_iterator_next (iter);
991 g_object_unref (iter);
993 /* We do not need to do it the first time
994 because it's automatically done by the tree
996 if (G_UNLIKELY (!first_time))
997 tny_folder_poke_status (TNY_FOLDER (folder));
999 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
1000 g_object_unref (observer);
1003 g_object_unref (G_OBJECT (folder));
1006 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1010 tny_iterator_next (iter);
1013 did_a_cancel = FALSE;
1015 g_object_unref (G_OBJECT (iter));
1016 g_source_remove (timeout);
1018 if (new_headers->len > 0) {
1022 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1024 /* Apply message count limit */
1025 /* If the number of messages exceeds the maximum, ask the
1026 * user to download them all,
1027 * as per the UI spec "Retrieval Limits" section in 4.4:
1029 if (new_headers->len > info->retrieve_limit) {
1030 /* TODO: Ask the user, instead of just
1032 * mail_nc_msg_count_limit_exceeded, with 'Get
1033 * all' and 'Newest only' buttons. */
1034 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1035 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1036 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1037 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1038 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1043 priv->total = MIN (new_headers->len, info->retrieve_limit);
1044 while (msg_num < priv->total) {
1046 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1047 TnyFolder *folder = tny_header_get_folder (header);
1048 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1049 ModestMailOperationState *state;
1053 /* We can not just use the mail operation because the
1054 values of done and total could change before the
1056 state = modest_mail_operation_clone_state (info->mail_op);
1057 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1058 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1059 pair, (GDestroyNotify) modest_pair_free);
1061 g_object_unref (msg);
1062 g_object_unref (folder);
1066 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1067 g_ptr_array_free (new_headers, FALSE);
1071 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1074 if (priv->account != NULL)
1075 g_object_unref (priv->account);
1076 priv->account = g_object_ref (info->transport_account);
1078 send_queue = modest_runtime_get_send_queue (info->transport_account);
1080 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1081 modest_tny_send_queue_try_to_send (send_queue);
1082 g_source_remove (timeout);
1084 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1085 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1086 "cannot create a send queue for %s\n",
1087 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1088 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1091 /* Check if the operation was a success */
1093 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1095 /* Update the last updated key */
1096 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1097 set_last_updated_idle,
1098 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1099 (GDestroyNotify) g_free);
1103 /* Notify about operation end. Note that the info could be
1104 freed before this idle happens, but the mail operation will
1106 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1108 if (info->callback) {
1109 /* This thread is not in the main lock */
1110 gdk_threads_enter ();
1111 info->callback (info->mail_op,
1112 (new_headers) ? new_headers->len : 0,
1114 gdk_threads_leave ();
1118 g_object_unref (query);
1119 g_object_unref (all_folders);
1120 g_object_unref (info->account);
1121 g_object_unref (info->transport_account);
1122 g_free (info->retrieve_type);
1123 g_slice_free (UpdateAccountInfo, info);
1131 modest_mail_operation_update_account (ModestMailOperation *self,
1132 const gchar *account_name,
1133 UpdateAccountCallback callback,
1137 UpdateAccountInfo *info;
1138 ModestMailOperationPrivate *priv;
1139 ModestAccountMgr *mgr;
1140 TnyStoreAccount *modest_account;
1141 TnyTransportAccount *transport_account;
1143 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1144 g_return_val_if_fail (account_name, FALSE);
1146 /* Init mail operation. Set total and done to 0, and do not
1147 update them, this way the progress objects will know that
1148 we have no clue about the number of the objects */
1149 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1152 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1154 /* Make sure that we have a connection, and request one
1156 * TODO: Is there some way to trigger this for every attempt to
1157 * use the network? */
1158 if (!modest_platform_connect_and_wait(NULL))
1161 /* Get the Modest account */
1162 modest_account = (TnyStoreAccount *)
1163 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1165 TNY_ACCOUNT_TYPE_STORE);
1167 if (!modest_account) {
1168 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1169 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1170 "cannot get tny store account for %s\n", account_name);
1175 /* Get the transport account, we can not do it in the thread
1176 due to some problems with dbus */
1177 transport_account = (TnyTransportAccount *)
1178 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1180 if (!transport_account) {
1181 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1182 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1183 "cannot get tny transport account for %s\n", account_name);
1187 /* Create the helper object */
1188 info = g_slice_new (UpdateAccountInfo);
1189 info->mail_op = self;
1190 info->account = modest_account;
1191 info->transport_account = transport_account;
1192 info->callback = callback;
1193 info->user_data = user_data;
1195 /* Get the message size limit */
1196 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1197 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1198 if (info->max_size == 0)
1199 info->max_size = G_MAXINT;
1201 info->max_size = info->max_size * KB;
1203 /* Get per-account retrieval type */
1204 mgr = modest_runtime_get_account_mgr ();
1205 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1206 MODEST_ACCOUNT_RETRIEVE, FALSE);
1208 /* Get per-account message amount retrieval limit */
1209 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1210 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1211 if (info->retrieve_limit == 0)
1212 info->retrieve_limit = G_MAXINT;
1214 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1216 /* Set account busy */
1217 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1218 priv->account_name = g_strdup(account_name);
1220 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1225 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1227 callback (self, 0, user_data);
1228 modest_mail_operation_notify_end (self, FALSE);
1232 /* ******************************************************************* */
1233 /* ************************** STORE ACTIONS ************************* */
1234 /* ******************************************************************* */
1238 modest_mail_operation_create_folder (ModestMailOperation *self,
1239 TnyFolderStore *parent,
1242 ModestMailOperationPrivate *priv;
1243 TnyFolder *new_folder = NULL;
1245 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1246 g_return_val_if_fail (name, NULL);
1248 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1251 if (TNY_IS_FOLDER (parent)) {
1252 /* Check folder rules */
1253 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1254 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1255 /* Set status failed and set an error */
1256 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1257 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1258 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1259 _("mail_in_ui_folder_create_error"));
1264 /* Create the folder */
1265 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1266 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1268 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1271 /* Notify about operation end */
1272 modest_mail_operation_notify_end (self, FALSE);
1278 modest_mail_operation_remove_folder (ModestMailOperation *self,
1280 gboolean remove_to_trash)
1282 TnyAccount *account;
1283 ModestMailOperationPrivate *priv;
1284 ModestTnyFolderRules rules;
1286 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1287 g_return_if_fail (TNY_IS_FOLDER (folder));
1289 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1291 /* Check folder rules */
1292 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1293 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1294 /* Set status failed and set an error */
1295 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1296 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1297 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1298 _("mail_in_ui_folder_delete_error"));
1302 /* Get the account */
1303 account = modest_tny_folder_get_account (folder);
1304 priv->account = g_object_ref(account);
1306 /* Delete folder or move to trash */
1307 if (remove_to_trash) {
1308 TnyFolder *trash_folder = NULL;
1309 trash_folder = modest_tny_account_get_special_folder (account,
1310 TNY_FOLDER_TYPE_TRASH);
1311 /* TODO: error_handling */
1312 modest_mail_operation_xfer_folder (self, folder,
1313 TNY_FOLDER_STORE (trash_folder), TRUE);
1315 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1317 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1318 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1321 g_object_unref (G_OBJECT (parent));
1323 g_object_unref (G_OBJECT (account));
1326 /* Notify about operation end */
1327 modest_mail_operation_notify_end (self, FALSE);
1331 transfer_folder_status_cb (GObject *obj,
1335 ModestMailOperation *self;
1336 ModestMailOperationPrivate *priv;
1337 ModestMailOperationState *state;
1339 g_return_if_fail (status != NULL);
1340 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1342 self = MODEST_MAIL_OPERATION (user_data);
1343 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1345 if ((status->position == 1) && (status->of_total == 100))
1348 priv->done = status->position;
1349 priv->total = status->of_total;
1351 state = modest_mail_operation_clone_state (self);
1352 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1353 g_slice_free (ModestMailOperationState, state);
1358 transfer_folder_cb (TnyFolder *folder,
1359 TnyFolderStore *into,
1361 TnyFolder *new_folder,
1365 ModestMailOperation *self = NULL;
1366 ModestMailOperationPrivate *priv = NULL;
1368 self = MODEST_MAIL_OPERATION (user_data);
1370 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1373 priv->error = g_error_copy (*err);
1375 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1376 } else if (cancelled) {
1377 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1378 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1379 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1380 _("Transference of %s was cancelled."),
1381 tny_folder_get_name (folder));
1384 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1388 g_object_unref (folder);
1389 g_object_unref (into);
1390 if (new_folder != NULL)
1391 g_object_unref (new_folder);
1393 /* Notify about operation end */
1394 modest_mail_operation_notify_end (self, TRUE);
1398 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1400 TnyFolderStore *parent,
1401 gboolean delete_original)
1403 ModestMailOperationPrivate *priv = NULL;
1404 ModestTnyFolderRules parent_rules, rules;
1406 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1407 g_return_if_fail (TNY_IS_FOLDER (folder));
1408 g_return_if_fail (TNY_IS_FOLDER (parent));
1410 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1412 /* Get account and set it into mail_operation */
1413 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1414 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1416 /* Get folder rules */
1417 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1418 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1420 if (!TNY_IS_FOLDER_STORE (parent)) {
1424 /* The moveable restriction is applied also to copy operation */
1425 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1426 /* Set status failed and set an error */
1427 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1428 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1429 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1430 _("mail_in_ui_folder_move_target_error"));
1432 /* Notify the queue */
1433 modest_mail_operation_notify_end (self, FALSE);
1434 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1435 /* Set status failed and set an error */
1436 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1437 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1438 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1439 _("FIXME: parent folder does not accept new folders"));
1441 /* Notify the queue */
1442 modest_mail_operation_notify_end (self, FALSE);
1444 /* Pick references for async calls */
1445 g_object_ref (folder);
1446 g_object_ref (parent);
1448 /* Move/Copy folder */
1449 tny_folder_copy_async (folder,
1451 tny_folder_get_name (folder),
1454 transfer_folder_status_cb,
1460 modest_mail_operation_rename_folder (ModestMailOperation *self,
1464 ModestMailOperationPrivate *priv;
1465 ModestTnyFolderRules rules;
1467 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1468 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1469 g_return_if_fail (name);
1471 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1473 /* Get account and set it into mail_operation */
1474 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1476 /* Check folder rules */
1477 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1478 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1479 /* Set status failed and set an error */
1480 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1481 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1482 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1483 _("FIXME: unable to rename"));
1485 /* Notify about operation end */
1486 modest_mail_operation_notify_end (self, FALSE);
1488 /* Rename. Camel handles folder subscription/unsubscription */
1489 TnyFolderStore *into;
1491 into = tny_folder_get_folder_store (folder);
1492 tny_folder_copy_async (folder, into, name, TRUE,
1494 transfer_folder_status_cb,
1497 g_object_unref (into);
1502 /* ******************************************************************* */
1503 /* ************************** MSG ACTIONS ************************* */
1504 /* ******************************************************************* */
1506 void modest_mail_operation_get_msg (ModestMailOperation *self,
1508 GetMsgAsyncUserCallback user_callback,
1511 GetMsgAsyncHelper *helper = NULL;
1513 ModestMailOperationPrivate *priv;
1515 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1516 g_return_if_fail (TNY_IS_HEADER (header));
1518 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1519 folder = tny_header_get_folder (header);
1521 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1523 /* Get message from folder */
1525 /* Get account and set it into mail_operation */
1526 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1528 helper = g_slice_new0 (GetMsgAsyncHelper);
1529 helper->mail_op = self;
1530 helper->user_callback = user_callback;
1531 helper->user_data = user_data;
1532 helper->header = g_object_ref (header);
1534 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1536 g_object_unref (G_OBJECT (folder));
1538 /* Set status failed and set an error */
1539 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1540 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1541 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1542 _("Error trying to get a message. No folder found for header"));
1544 /* Notify the queue */
1545 modest_mail_operation_notify_end (self, FALSE);
1550 get_msg_cb (TnyFolder *folder,
1556 GetMsgAsyncHelper *helper = NULL;
1557 ModestMailOperation *self = NULL;
1558 ModestMailOperationPrivate *priv = NULL;
1560 helper = (GetMsgAsyncHelper *) user_data;
1561 g_return_if_fail (helper != NULL);
1562 self = helper->mail_op;
1563 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1564 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1566 /* Check errors and cancel */
1568 priv->error = g_error_copy (*error);
1569 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1573 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1574 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1575 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1576 _("Error trying to refresh the contents of %s"),
1577 tny_folder_get_name (folder));
1581 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1583 /* If user defined callback function was defined, call it */
1584 if (helper->user_callback) {
1585 /* This callback is called into an iddle by tinymail,
1586 and idles are not in the main lock */
1587 gdk_threads_enter ();
1588 helper->user_callback (self, helper->header, msg, helper->user_data);
1589 gdk_threads_leave ();
1594 g_object_unref (helper->header);
1595 g_slice_free (GetMsgAsyncHelper, helper);
1597 /* Notify about operation end */
1598 modest_mail_operation_notify_end (self, TRUE);
1602 get_msg_status_cb (GObject *obj,
1606 GetMsgAsyncHelper *helper = NULL;
1607 ModestMailOperation *self;
1608 ModestMailOperationPrivate *priv;
1609 ModestMailOperationState *state;
1611 g_return_if_fail (status != NULL);
1612 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1614 helper = (GetMsgAsyncHelper *) user_data;
1615 g_return_if_fail (helper != NULL);
1617 self = helper->mail_op;
1618 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1620 if ((status->position == 1) && (status->of_total == 100))
1626 state = modest_mail_operation_clone_state (self);
1627 state->bytes_done = status->position;
1628 state->bytes_total = status->of_total;
1629 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1630 g_slice_free (ModestMailOperationState, state);
1633 /****************************************************/
1635 ModestMailOperation *mail_op;
1637 GetMsgAsyncUserCallback user_callback;
1639 GDestroyNotify notify;
1643 GetMsgAsyncUserCallback user_callback;
1647 ModestMailOperation *mail_op;
1648 } NotifyGetMsgsInfo;
1652 * Used by get_msgs_full_thread to call the user_callback for each
1653 * message that has been read
1656 notify_get_msgs_full (gpointer data)
1658 NotifyGetMsgsInfo *info;
1660 info = (NotifyGetMsgsInfo *) data;
1662 /* Call the user callback. Idles are not in the main lock, so
1664 gdk_threads_enter ();
1665 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1666 gdk_threads_leave ();
1668 g_slice_free (NotifyGetMsgsInfo, info);
1674 * Used by get_msgs_full_thread to free al the thread resources and to
1675 * call the destroy function for the passed user_data
1678 get_msgs_full_destroyer (gpointer data)
1680 GetFullMsgsInfo *info;
1682 info = (GetFullMsgsInfo *) data;
1685 gdk_threads_enter ();
1686 info->notify (info->user_data);
1687 gdk_threads_leave ();
1691 g_object_unref (info->headers);
1692 g_slice_free (GetFullMsgsInfo, info);
1698 get_msgs_full_thread (gpointer thr_user_data)
1700 GetFullMsgsInfo *info;
1701 ModestMailOperationPrivate *priv = NULL;
1702 TnyIterator *iter = NULL;
1704 info = (GetFullMsgsInfo *) thr_user_data;
1705 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1707 iter = tny_list_create_iterator (info->headers);
1708 while (!tny_iterator_is_done (iter)) {
1712 header = TNY_HEADER (tny_iterator_get_current (iter));
1713 folder = tny_header_get_folder (header);
1715 /* Get message from folder */
1718 /* The callback will call it per each header */
1719 msg = tny_folder_get_msg (folder, header, &(priv->error));
1722 ModestMailOperationState *state;
1727 /* notify progress */
1728 state = modest_mail_operation_clone_state (info->mail_op);
1729 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1730 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1731 pair, (GDestroyNotify) modest_pair_free);
1733 /* The callback is the responsible for
1734 freeing the message */
1735 if (info->user_callback) {
1736 NotifyGetMsgsInfo *info_notify;
1737 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1738 info_notify->user_callback = info->user_callback;
1739 info_notify->mail_op = info->mail_op;
1740 info_notify->header = g_object_ref (header);
1741 info_notify->msg = g_object_ref (msg);
1742 info_notify->user_data = info->user_data;
1743 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1744 notify_get_msgs_full,
1747 g_object_unref (msg);
1750 /* Set status failed and set an error */
1751 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1752 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1753 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1754 "Error trying to get a message. No folder found for header");
1756 g_object_unref (header);
1757 tny_iterator_next (iter);
1760 /* Set operation status */
1761 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1762 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1764 /* Notify about operation end */
1765 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1767 /* Free thread resources. Will be called after all previous idles */
1768 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1774 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1775 TnyList *header_list,
1776 GetMsgAsyncUserCallback user_callback,
1778 GDestroyNotify notify)
1780 TnyHeader *header = NULL;
1781 TnyFolder *folder = NULL;
1783 ModestMailOperationPrivate *priv = NULL;
1784 GetFullMsgsInfo *info = NULL;
1785 gboolean size_ok = TRUE;
1787 TnyIterator *iter = NULL;
1789 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1791 /* Init mail operation */
1792 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1793 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1795 priv->total = tny_list_get_length(header_list);
1797 /* Get account and set it into mail_operation */
1798 if (tny_list_get_length (header_list) >= 1) {
1799 iter = tny_list_create_iterator (header_list);
1800 header = TNY_HEADER (tny_iterator_get_current (iter));
1801 folder = tny_header_get_folder (header);
1802 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1803 g_object_unref (header);
1804 g_object_unref (folder);
1806 if (tny_list_get_length (header_list) == 1) {
1807 g_object_unref (iter);
1812 /* Get msg size limit */
1813 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1814 MODEST_CONF_MSG_SIZE_LIMIT,
1817 g_clear_error (&(priv->error));
1818 max_size = G_MAXINT;
1820 max_size = max_size * KB;
1823 /* Check message size limits. If there is only one message
1824 always retrieve it */
1826 while (!tny_iterator_is_done (iter) && size_ok) {
1827 header = TNY_HEADER (tny_iterator_get_current (iter));
1828 if (tny_header_get_message_size (header) >= max_size)
1830 g_object_unref (header);
1831 tny_iterator_next (iter);
1833 g_object_unref (iter);
1837 /* Create the info */
1838 info = g_slice_new0 (GetFullMsgsInfo);
1839 info->mail_op = self;
1840 info->user_callback = user_callback;
1841 info->user_data = user_data;
1842 info->headers = g_object_ref (header_list);
1843 info->notify = notify;
1845 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1847 /* Set status failed and set an error */
1848 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1849 /* FIXME: the error msg is different for pop */
1850 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1851 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1852 _("emev_ni_ui_imap_msg_size_exceed_error"));
1853 /* Remove from queue and free resources */
1854 modest_mail_operation_notify_end (self, FALSE);
1862 modest_mail_operation_remove_msg (ModestMailOperation *self,
1864 gboolean remove_to_trash)
1867 ModestMailOperationPrivate *priv;
1869 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1870 g_return_if_fail (TNY_IS_HEADER (header));
1872 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1873 folder = tny_header_get_folder (header);
1875 /* Get account and set it into mail_operation */
1876 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1878 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1880 /* Delete or move to trash */
1881 if (remove_to_trash) {
1882 TnyFolder *trash_folder;
1883 TnyStoreAccount *store_account;
1885 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1886 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1887 TNY_FOLDER_TYPE_TRASH);
1892 headers = tny_simple_list_new ();
1893 tny_list_append (headers, G_OBJECT (header));
1894 g_object_unref (header);
1897 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1898 g_object_unref (headers);
1899 /* g_object_unref (trash_folder); */
1901 ModestMailOperationPrivate *priv;
1903 /* Set status failed and set an error */
1904 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1905 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1906 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1907 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1908 _("Error trying to delete a message. Trash folder not found"));
1911 g_object_unref (G_OBJECT (store_account));
1913 tny_folder_remove_msg (folder, header, &(priv->error));
1915 tny_folder_sync(folder, TRUE, &(priv->error));
1920 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1922 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1925 g_object_unref (G_OBJECT (folder));
1927 /* Notify about operation end */
1928 modest_mail_operation_notify_end (self, FALSE);
1932 transfer_msgs_status_cb (GObject *obj,
1936 XFerMsgAsyncHelper *helper = NULL;
1937 ModestMailOperation *self;
1938 ModestMailOperationPrivate *priv;
1939 ModestMailOperationState *state;
1942 g_return_if_fail (status != NULL);
1943 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1945 helper = (XFerMsgAsyncHelper *) user_data;
1946 g_return_if_fail (helper != NULL);
1948 self = helper->mail_op;
1949 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1951 if ((status->position == 1) && (status->of_total == 100))
1954 priv->done = status->position;
1955 priv->total = status->of_total;
1957 state = modest_mail_operation_clone_state (self);
1958 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1959 g_slice_free (ModestMailOperationState, state);
1964 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1966 XFerMsgAsyncHelper *helper;
1967 ModestMailOperation *self;
1968 ModestMailOperationPrivate *priv;
1970 helper = (XFerMsgAsyncHelper *) user_data;
1971 self = helper->mail_op;
1973 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1976 priv->error = g_error_copy (*err);
1978 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1979 } else if (cancelled) {
1980 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1981 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1982 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1983 _("Error trying to refresh the contents of %s"),
1984 tny_folder_get_name (folder));
1987 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1990 /* If user defined callback function was defined, call it */
1991 if (helper->user_callback) {
1992 gdk_threads_enter ();
1993 helper->user_callback (priv->source, helper->user_data);
1994 gdk_threads_leave ();
1998 g_object_unref (helper->headers);
1999 g_object_unref (helper->dest_folder);
2000 g_object_unref (helper->mail_op);
2001 g_slice_free (XFerMsgAsyncHelper, helper);
2002 g_object_unref (folder);
2004 /* Notify about operation end */
2005 modest_mail_operation_notify_end (self, TRUE);
2009 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2012 gboolean delete_original,
2013 XferMsgsAsynUserCallback user_callback,
2016 ModestMailOperationPrivate *priv;
2018 TnyFolder *src_folder;
2019 XFerMsgAsyncHelper *helper;
2021 ModestTnyFolderRules rules;
2023 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2024 g_return_if_fail (TNY_IS_LIST (headers));
2025 g_return_if_fail (TNY_IS_FOLDER (folder));
2027 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2030 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2032 /* Apply folder rules */
2033 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2034 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2035 /* Set status failed and set an error */
2036 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2037 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2038 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2039 _("FIXME: folder does not accept msgs"));
2040 /* Notify the queue */
2041 modest_mail_operation_notify_end (self, FALSE);
2045 /* Create the helper */
2046 helper = g_slice_new0 (XFerMsgAsyncHelper);
2047 helper->mail_op = g_object_ref(self);
2048 helper->dest_folder = g_object_ref(folder);
2049 helper->headers = g_object_ref(headers);
2050 helper->user_callback = user_callback;
2051 helper->user_data = user_data;
2053 /* Get source folder */
2054 iter = tny_list_create_iterator (headers);
2055 header = TNY_HEADER (tny_iterator_get_current (iter));
2056 src_folder = tny_header_get_folder (header);
2057 g_object_unref (header);
2058 g_object_unref (iter);
2060 /* Get account and set it into mail_operation */
2061 priv->account = modest_tny_folder_get_account (src_folder);
2063 /* Transfer messages */
2064 tny_folder_transfer_msgs_async (src_folder,
2069 transfer_msgs_status_cb,
2075 on_refresh_folder (TnyFolder *folder,
2080 RefreshAsyncHelper *helper = NULL;
2081 ModestMailOperation *self = NULL;
2082 ModestMailOperationPrivate *priv = NULL;
2084 helper = (RefreshAsyncHelper *) user_data;
2085 self = helper->mail_op;
2086 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2089 priv->error = g_error_copy (*error);
2090 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2095 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2096 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2097 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2098 _("Error trying to refresh the contents of %s"),
2099 tny_folder_get_name (folder));
2103 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2106 /* Call user defined callback, if it exists */
2107 if (helper->user_callback) {
2108 gdk_threads_enter ();
2109 helper->user_callback (priv->source, folder, helper->user_data);
2110 gdk_threads_leave ();
2114 g_object_unref (helper->mail_op);
2115 g_slice_free (RefreshAsyncHelper, helper);
2116 g_object_unref (folder);
2118 /* Notify about operation end */
2119 modest_mail_operation_notify_end (self, TRUE);
2123 on_refresh_folder_status_update (GObject *obj,
2127 RefreshAsyncHelper *helper = NULL;
2128 ModestMailOperation *self = NULL;
2129 ModestMailOperationPrivate *priv = NULL;
2130 ModestMailOperationState *state;
2132 g_return_if_fail (user_data != NULL);
2133 g_return_if_fail (status != NULL);
2134 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2136 helper = (RefreshAsyncHelper *) user_data;
2137 self = helper->mail_op;
2138 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2140 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2142 priv->done = status->position;
2143 priv->total = status->of_total;
2145 state = modest_mail_operation_clone_state (self);
2146 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2147 g_slice_free (ModestMailOperationState, state);
2151 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2153 RefreshAsyncUserCallback user_callback,
2156 ModestMailOperationPrivate *priv = NULL;
2157 RefreshAsyncHelper *helper = NULL;
2159 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2161 /* Pick a reference */
2162 g_object_ref (folder);
2164 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2166 /* Get account and set it into mail_operation */
2167 priv->account = modest_tny_folder_get_account (folder);
2169 /* Create the helper */
2170 helper = g_slice_new0 (RefreshAsyncHelper);
2171 helper->mail_op = g_object_ref(self);
2172 helper->user_callback = user_callback;
2173 helper->user_data = user_data;
2175 /* Refresh the folder. TODO: tinymail could issue a status
2176 updates before the callback call then this could happen. We
2177 must review the design */
2178 tny_folder_refresh_async (folder,
2180 on_refresh_folder_status_update,
2186 * It's used by the mail operation queue to notify the observers
2187 * attached to that signal that the operation finished. We need to use
2188 * that because tinymail does not give us the progress of a given
2189 * operation when it finishes (it directly calls the operation
2193 modest_mail_operation_notify_end (ModestMailOperation *self,
2196 ModestMailOperationState *state;
2197 ModestMailOperationPrivate *priv = NULL;
2199 g_return_if_fail (self);
2201 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2204 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2208 /* Set the account back to not busy */
2209 if (priv->account_name) {
2210 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2211 priv->account_name, FALSE);
2212 g_free(priv->account_name);
2213 priv->account_name = NULL;
2216 /* Notify the observers about the mail opertation end */
2217 state = modest_mail_operation_clone_state (self);
2219 gdk_threads_enter ();
2220 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2222 gdk_threads_leave ();
2223 g_slice_free (ModestMailOperationState, state);