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 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
799 ModestMailOperationState *state;
801 state = modest_mail_operation_clone_state (mail_op);
802 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
803 g_slice_free (ModestMailOperationState, state);
809 * Issues the "progress-changed" signal and removes the timer. It uses
810 * a lock to ensure that the progress information of the mail
811 * operation is not modified while there are notifications pending
814 idle_notify_progress_once (gpointer data)
818 pair = (ModestPair *) data;
820 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
822 /* Free the state and the reference to the mail operation */
823 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
824 g_object_unref (pair->first);
830 * Used by update_account_thread to notify the queue from the main
831 * loop. We call it inside an idle call to achieve that
834 idle_notify_update_account_queue (gpointer data)
836 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
837 ModestMailOperationPrivate *priv = NULL;
839 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
841 /* Do not need to block, the notify end will do it for us */
842 modest_mail_operation_notify_end (mail_op, TRUE);
843 g_object_unref (mail_op);
849 compare_headers_by_date (gconstpointer a,
852 TnyHeader **header1, **header2;
855 header1 = (TnyHeader **) a;
856 header2 = (TnyHeader **) b;
858 sent1 = tny_header_get_date_sent (*header1);
859 sent2 = tny_header_get_date_sent (*header2);
861 /* We want the most recent ones (greater time_t) at the
870 set_last_updated_idle (gpointer data)
872 gdk_threads_enter ();
874 /* It does not matter if the time is not exactly the same than
875 the time when this idle was called, it's just an
876 approximation and it won't be very different */
877 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
879 MODEST_ACCOUNT_LAST_UPDATED,
887 update_account_thread (gpointer thr_user_data)
889 static gboolean first_time = TRUE;
890 UpdateAccountInfo *info;
891 TnyList *all_folders = NULL;
892 GPtrArray *new_headers = NULL;
893 TnyIterator *iter = NULL;
894 TnyFolderStoreQuery *query = NULL;
895 ModestMailOperationPrivate *priv = NULL;
896 ModestTnySendQueue *send_queue = NULL;
898 info = (UpdateAccountInfo *) thr_user_data;
899 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
901 /* Get account and set it into mail_operation */
902 priv->account = g_object_ref (info->account);
905 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
906 * show any updates unless we do that
908 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
909 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
911 /* Get all the folders. We can do it synchronously because
912 we're already running in a different thread than the UI */
913 all_folders = tny_simple_list_new ();
914 query = tny_folder_store_query_new ();
915 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
916 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
921 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
925 iter = tny_list_create_iterator (all_folders);
926 while (!tny_iterator_is_done (iter)) {
927 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
929 recurse_folders (folder, query, all_folders);
930 tny_iterator_next (iter);
932 g_object_unref (G_OBJECT (iter));
934 /* Update status and notify. We need to call the notification
935 with a source function in order to call it from the main
936 loop. We need that in order not to get into trouble with
937 Gtk+. We use a timeout in order to provide more status
938 information, because the sync tinymail call does not
939 provide it for the moment */
940 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
942 /* Refresh folders */
943 new_headers = g_ptr_array_new ();
944 iter = tny_list_create_iterator (all_folders);
946 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
948 InternalFolderObserver *observer;
949 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
951 /* Refresh the folder */
952 /* Our observer receives notification of new emails during folder refreshes,
953 * so we can use observer->new_headers.
955 observer = g_object_new (internal_folder_observer_get_type (), NULL);
956 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
958 /* This gets the status information (headers) from the server.
959 * We use the blocking version, because we are already in a separate
963 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
964 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
967 /* If the retrieve type is full messages, refresh and get the messages */
968 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
970 iter = tny_list_create_iterator (observer->new_headers);
971 while (!tny_iterator_is_done (iter)) {
972 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
974 /* Apply per-message size limits */
975 if (tny_header_get_message_size (header) < info->max_size)
976 g_ptr_array_add (new_headers, g_object_ref (header));
978 g_object_unref (header);
979 tny_iterator_next (iter);
981 g_object_unref (iter);
983 /* We do not need to do it the first time
984 because it's automatically done by the tree
986 if (G_UNLIKELY (!first_time))
987 tny_folder_poke_status (TNY_FOLDER (folder));
989 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
990 g_object_unref (observer);
993 g_object_unref (G_OBJECT (folder));
996 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1000 tny_iterator_next (iter);
1003 did_a_cancel = FALSE;
1005 g_object_unref (G_OBJECT (iter));
1006 g_source_remove (timeout);
1008 if (new_headers->len > 0) {
1012 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1014 /* Apply message count limit */
1015 /* If the number of messages exceeds the maximum, ask the
1016 * user to download them all,
1017 * as per the UI spec "Retrieval Limits" section in 4.4:
1019 if (new_headers->len > info->retrieve_limit) {
1020 /* TODO: Ask the user, instead of just
1022 * mail_nc_msg_count_limit_exceeded, with 'Get
1023 * all' and 'Newest only' buttons. */
1024 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1025 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1026 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1027 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1028 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1033 priv->total = MIN (new_headers->len, info->retrieve_limit);
1034 while (msg_num < priv->total) {
1036 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1037 TnyFolder *folder = tny_header_get_folder (header);
1038 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1039 ModestMailOperationState *state;
1043 /* We can not just use the mail operation because the
1044 values of done and total could change before the
1046 state = modest_mail_operation_clone_state (info->mail_op);
1047 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1048 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1049 pair, (GDestroyNotify) modest_pair_free);
1051 g_object_unref (msg);
1052 g_object_unref (folder);
1056 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1057 g_ptr_array_free (new_headers, FALSE);
1061 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1064 if (priv->account != NULL)
1065 g_object_unref (priv->account);
1066 priv->account = g_object_ref (info->transport_account);
1068 send_queue = modest_runtime_get_send_queue (info->transport_account);
1070 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1071 modest_tny_send_queue_try_to_send (send_queue);
1072 g_source_remove (timeout);
1074 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1075 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1076 "cannot create a send queue for %s\n",
1077 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1078 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1081 /* Check if the operation was a success */
1083 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1085 /* Update the last updated key */
1086 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1087 set_last_updated_idle,
1088 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1089 (GDestroyNotify) g_free);
1093 /* Notify about operation end. Note that the info could be
1094 freed before this idle happens, but the mail operation will
1096 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1098 if (info->callback) {
1099 /* This thread is not in the main lock */
1100 gdk_threads_enter ();
1101 info->callback (info->mail_op,
1102 (new_headers) ? new_headers->len : 0,
1104 gdk_threads_leave ();
1108 g_object_unref (query);
1109 g_object_unref (all_folders);
1110 g_object_unref (info->account);
1111 g_object_unref (info->transport_account);
1112 g_free (info->retrieve_type);
1113 g_slice_free (UpdateAccountInfo, info);
1121 modest_mail_operation_update_account (ModestMailOperation *self,
1122 const gchar *account_name,
1123 UpdateAccountCallback callback,
1127 UpdateAccountInfo *info;
1128 ModestMailOperationPrivate *priv;
1129 ModestAccountMgr *mgr;
1130 TnyStoreAccount *modest_account;
1131 TnyTransportAccount *transport_account;
1133 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1134 g_return_val_if_fail (account_name, FALSE);
1136 /* Init mail operation. Set total and done to 0, and do not
1137 update them, this way the progress objects will know that
1138 we have no clue about the number of the objects */
1139 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1142 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1144 /* Make sure that we have a connection, and request one
1146 * TODO: Is there some way to trigger this for every attempt to
1147 * use the network? */
1148 if (!modest_platform_connect_and_wait(NULL))
1151 /* Get the Modest account */
1152 modest_account = (TnyStoreAccount *)
1153 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1155 TNY_ACCOUNT_TYPE_STORE);
1157 if (!modest_account) {
1158 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1159 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1160 "cannot get tny store account for %s\n", account_name);
1165 /* Get the transport account, we can not do it in the thread
1166 due to some problems with dbus */
1167 transport_account = (TnyTransportAccount *)
1168 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1170 if (!transport_account) {
1171 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1172 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1173 "cannot get tny transport account for %s\n", account_name);
1177 /* Create the helper object */
1178 info = g_slice_new (UpdateAccountInfo);
1179 info->mail_op = self;
1180 info->account = modest_account;
1181 info->transport_account = transport_account;
1182 info->callback = callback;
1183 info->user_data = user_data;
1185 /* Get the message size limit */
1186 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1187 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1188 if (info->max_size == 0)
1189 info->max_size = G_MAXINT;
1191 info->max_size = info->max_size * KB;
1193 /* Get per-account retrieval type */
1194 mgr = modest_runtime_get_account_mgr ();
1195 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1196 MODEST_ACCOUNT_RETRIEVE, FALSE);
1198 /* Get per-account message amount retrieval limit */
1199 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1200 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1201 if (info->retrieve_limit == 0)
1202 info->retrieve_limit = G_MAXINT;
1204 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1206 /* Set account busy */
1207 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1208 priv->account_name = g_strdup(account_name);
1210 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1215 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1217 callback (self, 0, user_data);
1218 modest_mail_operation_notify_end (self, FALSE);
1222 /* ******************************************************************* */
1223 /* ************************** STORE ACTIONS ************************* */
1224 /* ******************************************************************* */
1228 modest_mail_operation_create_folder (ModestMailOperation *self,
1229 TnyFolderStore *parent,
1232 ModestMailOperationPrivate *priv;
1233 TnyFolder *new_folder = NULL;
1235 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1236 g_return_val_if_fail (name, NULL);
1238 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1241 if (TNY_IS_FOLDER (parent)) {
1242 /* Check folder rules */
1243 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1244 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1245 /* Set status failed and set an error */
1246 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1247 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1248 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1249 _("mail_in_ui_folder_create_error"));
1254 /* Create the folder */
1255 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1256 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1258 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1261 /* Notify about operation end */
1262 modest_mail_operation_notify_end (self, FALSE);
1268 modest_mail_operation_remove_folder (ModestMailOperation *self,
1270 gboolean remove_to_trash)
1272 TnyAccount *account;
1273 ModestMailOperationPrivate *priv;
1274 ModestTnyFolderRules rules;
1276 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1277 g_return_if_fail (TNY_IS_FOLDER (folder));
1279 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1281 /* Check folder rules */
1282 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1283 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1284 /* Set status failed and set an error */
1285 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1286 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1287 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1288 _("mail_in_ui_folder_delete_error"));
1292 /* Get the account */
1293 account = modest_tny_folder_get_account (folder);
1294 priv->account = g_object_ref(account);
1296 /* Delete folder or move to trash */
1297 if (remove_to_trash) {
1298 TnyFolder *trash_folder = NULL;
1299 trash_folder = modest_tny_account_get_special_folder (account,
1300 TNY_FOLDER_TYPE_TRASH);
1301 /* TODO: error_handling */
1302 modest_mail_operation_xfer_folder (self, folder,
1303 TNY_FOLDER_STORE (trash_folder), TRUE);
1305 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1307 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1308 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1311 g_object_unref (G_OBJECT (parent));
1313 g_object_unref (G_OBJECT (account));
1316 /* Notify about operation end */
1317 modest_mail_operation_notify_end (self, FALSE);
1321 transfer_folder_status_cb (GObject *obj,
1325 ModestMailOperation *self;
1326 ModestMailOperationPrivate *priv;
1327 ModestMailOperationState *state;
1329 g_return_if_fail (status != NULL);
1330 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1332 self = MODEST_MAIL_OPERATION (user_data);
1333 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1335 if ((status->position == 1) && (status->of_total == 100))
1338 priv->done = status->position;
1339 priv->total = status->of_total;
1341 state = modest_mail_operation_clone_state (self);
1342 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1343 g_slice_free (ModestMailOperationState, state);
1348 transfer_folder_cb (TnyFolder *folder,
1349 TnyFolderStore *into,
1351 TnyFolder *new_folder,
1355 ModestMailOperation *self = NULL;
1356 ModestMailOperationPrivate *priv = NULL;
1358 self = MODEST_MAIL_OPERATION (user_data);
1360 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1363 priv->error = g_error_copy (*err);
1365 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1366 } else if (cancelled) {
1367 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1368 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1369 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1370 _("Transference of %s was cancelled."),
1371 tny_folder_get_name (folder));
1374 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1378 g_object_unref (folder);
1379 g_object_unref (into);
1381 /* Notify about operation end */
1382 modest_mail_operation_notify_end (self, TRUE);
1386 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1388 TnyFolderStore *parent,
1389 gboolean delete_original)
1391 ModestMailOperationPrivate *priv = NULL;
1392 ModestTnyFolderRules parent_rules, rules;
1394 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1395 g_return_if_fail (TNY_IS_FOLDER (folder));
1396 g_return_if_fail (TNY_IS_FOLDER (parent));
1398 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1400 /* Get account and set it into mail_operation */
1401 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1402 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1404 /* Get folder rules */
1405 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1406 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1408 if (!TNY_IS_FOLDER_STORE (parent)) {
1412 /* The moveable restriction is applied also to copy operation */
1413 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1414 /* Set status failed and set an error */
1415 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1416 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1417 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1418 _("mail_in_ui_folder_move_target_error"));
1420 /* Notify the queue */
1421 modest_mail_operation_notify_end (self, FALSE);
1422 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1423 /* Set status failed and set an error */
1424 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1425 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1426 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1427 _("FIXME: parent folder does not accept new folders"));
1429 /* Notify the queue */
1430 modest_mail_operation_notify_end (self, FALSE);
1432 /* Pick references for async calls */
1433 g_object_ref (folder);
1434 g_object_ref (parent);
1436 /* Move/Copy folder */
1437 tny_folder_copy_async (folder,
1439 tny_folder_get_name (folder),
1442 transfer_folder_status_cb,
1448 modest_mail_operation_rename_folder (ModestMailOperation *self,
1452 ModestMailOperationPrivate *priv;
1453 ModestTnyFolderRules rules;
1455 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1456 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1457 g_return_if_fail (name);
1459 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1461 /* Get account and set it into mail_operation */
1462 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1464 /* Check folder rules */
1465 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1466 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1467 /* Set status failed and set an error */
1468 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1469 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1470 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1471 _("FIXME: unable to rename"));
1473 /* Notify about operation end */
1474 modest_mail_operation_notify_end (self, FALSE);
1476 /* Rename. Camel handles folder subscription/unsubscription */
1477 TnyFolderStore *into;
1479 into = tny_folder_get_folder_store (folder);
1480 tny_folder_copy_async (folder, into, name, TRUE,
1482 transfer_folder_status_cb,
1485 g_object_unref (into);
1490 /* ******************************************************************* */
1491 /* ************************** MSG ACTIONS ************************* */
1492 /* ******************************************************************* */
1494 void modest_mail_operation_get_msg (ModestMailOperation *self,
1496 GetMsgAsyncUserCallback user_callback,
1499 GetMsgAsyncHelper *helper = NULL;
1501 ModestMailOperationPrivate *priv;
1503 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1504 g_return_if_fail (TNY_IS_HEADER (header));
1506 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1507 folder = tny_header_get_folder (header);
1509 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1511 /* Get message from folder */
1513 /* Get account and set it into mail_operation */
1514 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1516 helper = g_slice_new0 (GetMsgAsyncHelper);
1517 helper->mail_op = self;
1518 helper->user_callback = user_callback;
1519 helper->user_data = user_data;
1520 helper->header = g_object_ref (header);
1522 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1524 g_object_unref (G_OBJECT (folder));
1526 /* Set status failed and set an error */
1527 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1528 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1529 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1530 _("Error trying to get a message. No folder found for header"));
1532 /* Notify the queue */
1533 modest_mail_operation_notify_end (self, FALSE);
1538 get_msg_cb (TnyFolder *folder,
1544 GetMsgAsyncHelper *helper = NULL;
1545 ModestMailOperation *self = NULL;
1546 ModestMailOperationPrivate *priv = NULL;
1548 helper = (GetMsgAsyncHelper *) user_data;
1549 g_return_if_fail (helper != NULL);
1550 self = helper->mail_op;
1551 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1552 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1554 /* Check errors and cancel */
1556 priv->error = g_error_copy (*error);
1557 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1561 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1562 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1563 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1564 _("Error trying to refresh the contents of %s"),
1565 tny_folder_get_name (folder));
1569 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1571 /* If user defined callback function was defined, call it */
1572 if (helper->user_callback) {
1573 /* This callback is called into an iddle by tinymail,
1574 and idles are not in the main lock */
1575 gdk_threads_enter ();
1576 helper->user_callback (self, helper->header, msg, helper->user_data);
1577 gdk_threads_leave ();
1582 g_object_unref (helper->header);
1583 g_slice_free (GetMsgAsyncHelper, helper);
1585 /* Notify about operation end */
1586 modest_mail_operation_notify_end (self, TRUE);
1590 get_msg_status_cb (GObject *obj,
1594 GetMsgAsyncHelper *helper = NULL;
1595 ModestMailOperation *self;
1596 ModestMailOperationPrivate *priv;
1597 ModestMailOperationState *state;
1599 g_return_if_fail (status != NULL);
1600 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1602 helper = (GetMsgAsyncHelper *) user_data;
1603 g_return_if_fail (helper != NULL);
1605 self = helper->mail_op;
1606 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1608 if ((status->position == 1) && (status->of_total == 100))
1614 state = modest_mail_operation_clone_state (self);
1615 state->bytes_done = status->position;
1616 state->bytes_total = status->of_total;
1617 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1618 g_slice_free (ModestMailOperationState, state);
1621 /****************************************************/
1623 ModestMailOperation *mail_op;
1625 GetMsgAsyncUserCallback user_callback;
1627 GDestroyNotify notify;
1631 GetMsgAsyncUserCallback user_callback;
1635 ModestMailOperation *mail_op;
1636 } NotifyGetMsgsInfo;
1640 * Used by get_msgs_full_thread to call the user_callback for each
1641 * message that has been read
1644 notify_get_msgs_full (gpointer data)
1646 NotifyGetMsgsInfo *info;
1648 info = (NotifyGetMsgsInfo *) data;
1650 /* Call the user callback. Idles are not in the main lock, so
1652 gdk_threads_enter ();
1653 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1654 gdk_threads_leave ();
1656 g_slice_free (NotifyGetMsgsInfo, info);
1662 * Used by get_msgs_full_thread to free al the thread resources and to
1663 * call the destroy function for the passed user_data
1666 get_msgs_full_destroyer (gpointer data)
1668 GetFullMsgsInfo *info;
1670 info = (GetFullMsgsInfo *) data;
1673 gdk_threads_enter ();
1674 info->notify (info->user_data);
1675 gdk_threads_leave ();
1679 g_object_unref (info->headers);
1680 g_slice_free (GetFullMsgsInfo, info);
1686 get_msgs_full_thread (gpointer thr_user_data)
1688 GetFullMsgsInfo *info;
1689 ModestMailOperationPrivate *priv = NULL;
1690 TnyIterator *iter = NULL;
1692 info = (GetFullMsgsInfo *) thr_user_data;
1693 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1695 iter = tny_list_create_iterator (info->headers);
1696 while (!tny_iterator_is_done (iter)) {
1700 header = TNY_HEADER (tny_iterator_get_current (iter));
1701 folder = tny_header_get_folder (header);
1703 /* Get message from folder */
1706 /* The callback will call it per each header */
1707 msg = tny_folder_get_msg (folder, header, &(priv->error));
1710 ModestMailOperationState *state;
1715 /* notify progress */
1716 state = modest_mail_operation_clone_state (info->mail_op);
1717 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1718 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1719 pair, (GDestroyNotify) modest_pair_free);
1721 /* The callback is the responsible for
1722 freeing the message */
1723 if (info->user_callback) {
1724 NotifyGetMsgsInfo *info_notify;
1725 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1726 info_notify->user_callback = info->user_callback;
1727 info_notify->mail_op = info->mail_op;
1728 info_notify->header = g_object_ref (header);
1729 info_notify->msg = g_object_ref (msg);
1730 info_notify->user_data = info->user_data;
1731 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1732 notify_get_msgs_full,
1735 g_object_unref (msg);
1738 /* Set status failed and set an error */
1739 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1740 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1741 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1742 "Error trying to get a message. No folder found for header");
1744 g_object_unref (header);
1745 tny_iterator_next (iter);
1748 /* Set operation status */
1749 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1750 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1752 /* Notify about operation end */
1753 g_idle_add (idle_notify_update_account_queue, g_object_ref (info->mail_op));
1755 /* Free thread resources. Will be called after all previous idles */
1756 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1762 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1763 TnyList *header_list,
1764 GetMsgAsyncUserCallback user_callback,
1766 GDestroyNotify notify)
1768 TnyHeader *header = NULL;
1769 TnyFolder *folder = NULL;
1771 ModestMailOperationPrivate *priv = NULL;
1772 GetFullMsgsInfo *info = NULL;
1773 gboolean size_ok = TRUE;
1775 TnyIterator *iter = NULL;
1777 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1779 /* Init mail operation */
1780 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1781 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1783 priv->total = tny_list_get_length(header_list);
1785 /* Get account and set it into mail_operation */
1786 if (tny_list_get_length (header_list) >= 1) {
1787 iter = tny_list_create_iterator (header_list);
1788 header = TNY_HEADER (tny_iterator_get_current (iter));
1789 folder = tny_header_get_folder (header);
1790 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1791 g_object_unref (header);
1792 g_object_unref (folder);
1794 if (tny_list_get_length (header_list) == 1) {
1795 g_object_unref (iter);
1800 /* Get msg size limit */
1801 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1802 MODEST_CONF_MSG_SIZE_LIMIT,
1805 g_clear_error (&(priv->error));
1806 max_size = G_MAXINT;
1808 max_size = max_size * KB;
1811 /* Check message size limits. If there is only one message
1812 always retrieve it */
1814 while (!tny_iterator_is_done (iter) && size_ok) {
1815 header = TNY_HEADER (tny_iterator_get_current (iter));
1816 if (tny_header_get_message_size (header) >= max_size)
1818 g_object_unref (header);
1819 tny_iterator_next (iter);
1821 g_object_unref (iter);
1825 /* Create the info */
1826 info = g_slice_new0 (GetFullMsgsInfo);
1827 info->mail_op = self;
1828 info->user_callback = user_callback;
1829 info->user_data = user_data;
1830 info->headers = g_object_ref (header_list);
1831 info->notify = notify;
1833 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1835 /* Set status failed and set an error */
1836 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1837 /* FIXME: the error msg is different for pop */
1838 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1839 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1840 _("emev_ni_ui_imap_msg_size_exceed_error"));
1841 /* Remove from queue and free resources */
1842 modest_mail_operation_notify_end (self, FALSE);
1850 modest_mail_operation_remove_msg (ModestMailOperation *self,
1852 gboolean remove_to_trash)
1855 ModestMailOperationPrivate *priv;
1857 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1858 g_return_if_fail (TNY_IS_HEADER (header));
1860 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1861 folder = tny_header_get_folder (header);
1863 /* Get account and set it into mail_operation */
1864 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1866 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1868 /* Delete or move to trash */
1869 if (remove_to_trash) {
1870 TnyFolder *trash_folder;
1871 TnyStoreAccount *store_account;
1873 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1874 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1875 TNY_FOLDER_TYPE_TRASH);
1880 headers = tny_simple_list_new ();
1881 tny_list_append (headers, G_OBJECT (header));
1882 g_object_unref (header);
1885 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1886 g_object_unref (headers);
1887 /* g_object_unref (trash_folder); */
1889 ModestMailOperationPrivate *priv;
1891 /* Set status failed and set an error */
1892 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1893 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1894 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1895 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1896 _("Error trying to delete a message. Trash folder not found"));
1899 g_object_unref (G_OBJECT (store_account));
1901 tny_folder_remove_msg (folder, header, &(priv->error));
1903 tny_folder_sync(folder, TRUE, &(priv->error));
1908 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1910 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1913 g_object_unref (G_OBJECT (folder));
1915 /* Notify about operation end */
1916 modest_mail_operation_notify_end (self, FALSE);
1920 transfer_msgs_status_cb (GObject *obj,
1924 XFerMsgAsyncHelper *helper = NULL;
1925 ModestMailOperation *self;
1926 ModestMailOperationPrivate *priv;
1927 ModestMailOperationState *state;
1930 g_return_if_fail (status != NULL);
1931 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1933 helper = (XFerMsgAsyncHelper *) user_data;
1934 g_return_if_fail (helper != NULL);
1936 self = helper->mail_op;
1937 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1939 if ((status->position == 1) && (status->of_total == 100))
1942 priv->done = status->position;
1943 priv->total = status->of_total;
1945 state = modest_mail_operation_clone_state (self);
1946 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1947 g_slice_free (ModestMailOperationState, state);
1952 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1954 XFerMsgAsyncHelper *helper;
1955 ModestMailOperation *self;
1956 ModestMailOperationPrivate *priv;
1958 helper = (XFerMsgAsyncHelper *) user_data;
1959 self = helper->mail_op;
1961 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1964 priv->error = g_error_copy (*err);
1966 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1967 } else if (cancelled) {
1968 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1969 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1970 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1971 _("Error trying to refresh the contents of %s"),
1972 tny_folder_get_name (folder));
1975 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1978 /* If user defined callback function was defined, call it */
1979 if (helper->user_callback) {
1980 gdk_threads_enter ();
1981 helper->user_callback (priv->source, helper->user_data);
1982 gdk_threads_leave ();
1986 g_object_unref (helper->headers);
1987 g_object_unref (helper->dest_folder);
1988 g_object_unref (helper->mail_op);
1989 g_slice_free (XFerMsgAsyncHelper, helper);
1990 g_object_unref (folder);
1992 /* Notify about operation end */
1993 modest_mail_operation_notify_end (self, TRUE);
1997 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2000 gboolean delete_original,
2001 XferMsgsAsynUserCallback user_callback,
2004 ModestMailOperationPrivate *priv;
2006 TnyFolder *src_folder;
2007 XFerMsgAsyncHelper *helper;
2009 ModestTnyFolderRules rules;
2011 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2012 g_return_if_fail (TNY_IS_LIST (headers));
2013 g_return_if_fail (TNY_IS_FOLDER (folder));
2015 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2018 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2020 /* Apply folder rules */
2021 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2022 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2023 /* Set status failed and set an error */
2024 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2025 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2026 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2027 _("FIXME: folder does not accept msgs"));
2028 /* Notify the queue */
2029 modest_mail_operation_notify_end (self, FALSE);
2033 /* Create the helper */
2034 helper = g_slice_new0 (XFerMsgAsyncHelper);
2035 helper->mail_op = g_object_ref(self);
2036 helper->dest_folder = g_object_ref(folder);
2037 helper->headers = g_object_ref(headers);
2038 helper->user_callback = user_callback;
2039 helper->user_data = user_data;
2041 /* Get source folder */
2042 iter = tny_list_create_iterator (headers);
2043 header = TNY_HEADER (tny_iterator_get_current (iter));
2044 src_folder = tny_header_get_folder (header);
2045 g_object_unref (header);
2046 g_object_unref (iter);
2048 /* Get account and set it into mail_operation */
2049 priv->account = modest_tny_folder_get_account (src_folder);
2051 /* Transfer messages */
2052 tny_folder_transfer_msgs_async (src_folder,
2057 transfer_msgs_status_cb,
2063 on_refresh_folder (TnyFolder *folder,
2068 RefreshAsyncHelper *helper = NULL;
2069 ModestMailOperation *self = NULL;
2070 ModestMailOperationPrivate *priv = NULL;
2072 helper = (RefreshAsyncHelper *) user_data;
2073 self = helper->mail_op;
2074 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2077 priv->error = g_error_copy (*error);
2078 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2083 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2084 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2085 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2086 _("Error trying to refresh the contents of %s"),
2087 tny_folder_get_name (folder));
2091 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2094 /* Call user defined callback, if it exists */
2095 if (helper->user_callback) {
2096 gdk_threads_enter ();
2097 helper->user_callback (priv->source, folder, helper->user_data);
2098 gdk_threads_leave ();
2102 g_object_unref (helper->mail_op);
2103 g_slice_free (RefreshAsyncHelper, helper);
2104 g_object_unref (folder);
2106 /* Notify about operation end */
2107 modest_mail_operation_notify_end (self, TRUE);
2111 on_refresh_folder_status_update (GObject *obj,
2115 RefreshAsyncHelper *helper = NULL;
2116 ModestMailOperation *self = NULL;
2117 ModestMailOperationPrivate *priv = NULL;
2118 ModestMailOperationState *state;
2120 g_return_if_fail (user_data != NULL);
2121 g_return_if_fail (status != NULL);
2122 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2124 helper = (RefreshAsyncHelper *) user_data;
2125 self = helper->mail_op;
2126 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2128 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2130 priv->done = status->position;
2131 priv->total = status->of_total;
2133 state = modest_mail_operation_clone_state (self);
2134 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2135 g_slice_free (ModestMailOperationState, state);
2139 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2141 RefreshAsyncUserCallback user_callback,
2144 ModestMailOperationPrivate *priv = NULL;
2145 RefreshAsyncHelper *helper = NULL;
2147 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2149 /* Pick a reference */
2150 g_object_ref (folder);
2152 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2154 /* Get account and set it into mail_operation */
2155 priv->account = modest_tny_folder_get_account (folder);
2157 /* Create the helper */
2158 helper = g_slice_new0 (RefreshAsyncHelper);
2159 helper->mail_op = g_object_ref(self);
2160 helper->user_callback = user_callback;
2161 helper->user_data = user_data;
2163 /* Refresh the folder. TODO: tinymail could issue a status
2164 updates before the callback call then this could happen. We
2165 must review the design */
2166 tny_folder_refresh_async (folder,
2168 on_refresh_folder_status_update,
2174 * It's used by the mail operation queue to notify the observers
2175 * attached to that signal that the operation finished. We need to use
2176 * that because tinymail does not give us the progress of a given
2177 * operation when it finishes (it directly calls the operation
2181 modest_mail_operation_notify_end (ModestMailOperation *self,
2184 ModestMailOperationState *state;
2185 ModestMailOperationPrivate *priv = NULL;
2187 g_return_if_fail (self);
2189 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2192 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2196 /* Set the account back to not busy */
2197 if (priv->account_name) {
2198 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr(),
2199 priv->account_name, FALSE);
2200 g_free(priv->account_name);
2201 priv->account_name = NULL;
2204 /* Notify the observers about the mail opertation end */
2205 state = modest_mail_operation_clone_state (self);
2207 gdk_threads_enter ();
2208 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2210 gdk_threads_leave ();
2211 g_slice_free (ModestMailOperationState, state);