1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize (GObject *obj);
63 static void get_msg_cb (TnyFolder *folder,
69 static void get_msg_status_cb (GObject *obj,
73 static void modest_mail_operation_notify_end (ModestMailOperation *self);
75 static gboolean did_a_cancel = FALSE;
77 enum _ModestMailOperationSignals
79 PROGRESS_CHANGED_SIGNAL,
84 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
85 struct _ModestMailOperationPrivate {
91 ErrorCheckingUserCallback error_checking;
92 gpointer error_checking_user_data;
93 ModestMailOperationStatus status;
94 ModestMailOperationTypeOperation op_type;
97 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
98 MODEST_TYPE_MAIL_OPERATION, \
99 ModestMailOperationPrivate))
101 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
102 priv->status = new_status;\
105 typedef struct _GetMsgAsyncHelper {
106 ModestMailOperation *mail_op;
107 GetMsgAsyncUserCallback user_callback;
112 typedef struct _XFerMsgAsyncHelper
114 ModestMailOperation *mail_op;
116 TnyFolder *dest_folder;
117 XferMsgsAsynUserCallback user_callback;
119 } XFerMsgAsyncHelper;
122 static GObjectClass *parent_class = NULL;
124 static guint signals[NUM_SIGNALS] = {0};
127 modest_mail_operation_get_type (void)
129 static GType my_type = 0;
131 static const GTypeInfo my_info = {
132 sizeof(ModestMailOperationClass),
133 NULL, /* base init */
134 NULL, /* base finalize */
135 (GClassInitFunc) modest_mail_operation_class_init,
136 NULL, /* class finalize */
137 NULL, /* class data */
138 sizeof(ModestMailOperation),
140 (GInstanceInitFunc) modest_mail_operation_init,
143 my_type = g_type_register_static (G_TYPE_OBJECT,
144 "ModestMailOperation",
151 modest_mail_operation_class_init (ModestMailOperationClass *klass)
153 GObjectClass *gobject_class;
154 gobject_class = (GObjectClass*) klass;
156 parent_class = g_type_class_peek_parent (klass);
157 gobject_class->finalize = modest_mail_operation_finalize;
159 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
162 * ModestMailOperation::progress-changed
163 * @self: the #MailOperation that emits the signal
164 * @user_data: user data set when the signal handler was connected
166 * Emitted when the progress of a mail operation changes
168 signals[PROGRESS_CHANGED_SIGNAL] =
169 g_signal_new ("progress-changed",
170 G_TYPE_FROM_CLASS (gobject_class),
172 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
174 g_cclosure_marshal_VOID__POINTER,
175 G_TYPE_NONE, 1, G_TYPE_POINTER);
180 modest_mail_operation_init (ModestMailOperation *obj)
182 ModestMailOperationPrivate *priv;
184 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
186 priv->account = NULL;
187 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
188 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
193 priv->error_checking = NULL;
194 priv->error_checking_user_data = NULL;
198 modest_mail_operation_finalize (GObject *obj)
200 ModestMailOperationPrivate *priv;
202 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
205 g_error_free (priv->error);
209 g_object_unref (priv->source);
213 g_object_unref (priv->account);
214 priv->account = NULL;
218 G_OBJECT_CLASS(parent_class)->finalize (obj);
222 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
225 ModestMailOperation *obj;
226 ModestMailOperationPrivate *priv;
228 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
229 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
231 priv->op_type = op_type;
233 priv->source = g_object_ref(source);
239 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
241 ErrorCheckingUserCallback error_handler,
244 ModestMailOperation *obj;
245 ModestMailOperationPrivate *priv;
247 obj = modest_mail_operation_new (op_type, source);
248 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
250 g_return_val_if_fail (error_handler != NULL, obj);
251 priv->error_checking = error_handler;
257 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
259 ModestMailOperationPrivate *priv;
261 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
262 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
264 if (priv->error_checking != NULL)
265 priv->error_checking (self, priv->error_checking_user_data);
269 ModestMailOperationTypeOperation
270 modest_mail_operation_get_type_operation (ModestMailOperation *self)
272 ModestMailOperationPrivate *priv;
274 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
276 return priv->op_type;
280 modest_mail_operation_is_mine (ModestMailOperation *self,
283 ModestMailOperationPrivate *priv;
285 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
286 if (priv->source == NULL) return FALSE;
288 return priv->source == me;
292 modest_mail_operation_get_source (ModestMailOperation *self)
294 ModestMailOperationPrivate *priv;
296 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
298 return g_object_ref (priv->source);
301 ModestMailOperationStatus
302 modest_mail_operation_get_status (ModestMailOperation *self)
304 ModestMailOperationPrivate *priv;
306 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
307 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
308 MODEST_MAIL_OPERATION_STATUS_INVALID);
310 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
315 modest_mail_operation_get_error (ModestMailOperation *self)
317 ModestMailOperationPrivate *priv;
319 g_return_val_if_fail (self, NULL);
320 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
322 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
327 modest_mail_operation_cancel (ModestMailOperation *self)
329 ModestMailOperationPrivate *priv;
331 if (!MODEST_IS_MAIL_OPERATION (self)) {
332 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
336 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
338 /* cancel current operation in account */
339 tny_account_cancel (priv->account);
344 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
346 /* Notify about operation end */
347 modest_mail_operation_notify_end (self);
353 modest_mail_operation_get_task_done (ModestMailOperation *self)
355 ModestMailOperationPrivate *priv;
357 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
359 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
364 modest_mail_operation_get_task_total (ModestMailOperation *self)
366 ModestMailOperationPrivate *priv;
368 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
370 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
375 modest_mail_operation_is_finished (ModestMailOperation *self)
377 ModestMailOperationPrivate *priv;
378 gboolean retval = FALSE;
380 if (!MODEST_IS_MAIL_OPERATION (self)) {
381 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
385 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
387 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
388 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
389 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
390 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
400 modest_mail_operation_get_id (ModestMailOperation *self)
402 ModestMailOperationPrivate *priv;
404 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
406 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
411 modest_mail_operation_set_id (ModestMailOperation *self,
414 ModestMailOperationPrivate *priv;
416 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
418 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
423 * Creates an image of the current state of a mail operation, the
424 * caller must free it
426 static ModestMailOperationState *
427 modest_mail_operation_clone_state (ModestMailOperation *self)
429 ModestMailOperationState *state;
430 ModestMailOperationPrivate *priv;
432 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
434 state = g_slice_new (ModestMailOperationState);
436 state->status = priv->status;
437 state->op_type = priv->op_type;
438 state->done = priv->done;
439 state->total = priv->total;
440 state->finished = modest_mail_operation_is_finished (self);
445 /* ******************************************************************* */
446 /* ************************** SEND ACTIONS ************************* */
447 /* ******************************************************************* */
450 modest_mail_operation_send_mail (ModestMailOperation *self,
451 TnyTransportAccount *transport_account,
454 TnySendQueue *send_queue = NULL;
455 ModestMailOperationPrivate *priv;
457 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
458 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
459 g_return_if_fail (TNY_IS_MSG (msg));
461 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
463 /* Get account and set it into mail_operation */
464 priv->account = g_object_ref (transport_account);
466 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
467 if (!TNY_IS_SEND_QUEUE(send_queue)) {
468 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
469 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
470 "modest: could not find send queue for account\n");
472 tny_send_queue_add (send_queue, msg, &(priv->error));
475 /* Notify about operation end */
476 modest_mail_operation_notify_end (self);
480 modest_mail_operation_send_new_mail (ModestMailOperation *self,
481 TnyTransportAccount *transport_account,
483 const gchar *from, const gchar *to,
484 const gchar *cc, const gchar *bcc,
485 const gchar *subject, const gchar *plain_body,
486 const gchar *html_body,
487 const GList *attachments_list,
488 TnyHeaderFlags priority_flags)
490 TnyMsg *new_msg = NULL;
491 TnyFolder *folder = NULL;
492 TnyHeader *header = NULL;
493 ModestMailOperationPrivate *priv = NULL;
495 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
496 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
498 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
500 /* Get account and set it into mail_operation */
501 priv->account = g_object_ref (transport_account);
503 /* Check parametters */
505 /* Set status failed and set an error */
506 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
507 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
508 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
509 _("Error trying to send a mail. You need to set at least one recipient"));
513 if (html_body == NULL) {
514 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
516 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
519 g_printerr ("modest: failed to create a new msg\n");
523 /* Set priority flags in message */
524 header = tny_msg_get_header (new_msg);
525 tny_header_set_flags (header, priority_flags);
527 /* Call mail operation */
528 modest_mail_operation_send_mail (self, transport_account, new_msg);
530 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
532 if (draft_msg != NULL) {
533 header = tny_msg_get_header (draft_msg);
534 /* Note: This can fail (with a warning) if the message is not really already in a folder,
535 * because this function requires it to have a UID. */
536 tny_folder_remove_msg (folder, header, NULL);
537 g_object_unref (header);
542 g_object_unref (G_OBJECT (new_msg));
546 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
547 TnyTransportAccount *transport_account,
549 const gchar *from, const gchar *to,
550 const gchar *cc, const gchar *bcc,
551 const gchar *subject, const gchar *plain_body,
552 const gchar *html_body,
553 const GList *attachments_list,
554 TnyHeaderFlags priority_flags)
557 TnyFolder *folder = NULL;
558 TnyHeader *header = NULL;
559 ModestMailOperationPrivate *priv = NULL;
561 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
562 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
564 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
566 /* Get account and set it into mail_operation */
567 priv->account = g_object_ref (transport_account);
569 if (html_body == NULL) {
570 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
572 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
575 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
576 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
577 "modest: failed to create a new msg\n");
581 /* add priority flags */
582 header = tny_msg_get_header (msg);
583 tny_header_set_flags (header, priority_flags);
585 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
587 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
588 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
589 "modest: failed to create a new msg\n");
593 if (draft_msg != NULL) {
594 header = tny_msg_get_header (draft_msg);
595 tny_folder_remove_msg (folder, header, NULL);
596 g_object_unref (header);
599 tny_folder_add_msg (folder, msg, &(priv->error));
605 g_object_unref (G_OBJECT(msg));
607 g_object_unref (G_OBJECT(folder));
609 modest_mail_operation_notify_end (self);
614 ModestMailOperation *mail_op;
615 TnyStoreAccount *account;
616 TnyTransportAccount *transport_account;
619 gchar *retrieve_type;
622 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
623 /* We use this folder observer to track the headers that have been
624 * added to a folder */
627 TnyList *new_headers;
628 } InternalFolderObserver;
632 } InternalFolderObserverClass;
634 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
636 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
637 internal_folder_observer,
639 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
643 foreach_add_item (gpointer header, gpointer user_data)
645 /* printf("DEBUG: %s: header subject=%s\n",
646 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
648 tny_list_prepend (TNY_LIST (user_data),
649 g_object_ref (G_OBJECT (header)));
652 /* This is the method that looks for new messages in a folder */
654 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
656 InternalFolderObserver *derived = (InternalFolderObserver *)self;
658 TnyFolderChangeChanged changed;
660 changed = tny_folder_change_get_changed (change);
662 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
665 /* Get added headers */
666 list = tny_simple_list_new ();
667 tny_folder_change_get_added_headers (change, list);
669 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
670 * __FUNCTION__, tny_list_get_length(list));
673 /* Add them to the folder observer */
674 tny_list_foreach (list, foreach_add_item,
675 derived->new_headers);
677 g_object_unref (G_OBJECT (list));
682 internal_folder_observer_init (InternalFolderObserver *self)
684 self->new_headers = tny_simple_list_new ();
687 internal_folder_observer_finalize (GObject *object)
689 InternalFolderObserver *self;
691 self = (InternalFolderObserver *) object;
692 g_object_unref (self->new_headers);
694 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
697 tny_folder_observer_init (TnyFolderObserverIface *iface)
699 iface->update_func = internal_folder_observer_update;
702 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
704 GObjectClass *object_class;
706 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
707 object_class = (GObjectClass*) klass;
708 object_class->finalize = internal_folder_observer_finalize;
714 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
717 TnyList *folders = tny_simple_list_new ();
719 tny_folder_store_get_folders (store, folders, query, NULL);
720 iter = tny_list_create_iterator (folders);
722 while (!tny_iterator_is_done (iter)) {
724 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
726 tny_list_prepend (all_folders, G_OBJECT (folder));
727 recurse_folders (folder, query, all_folders);
728 g_object_unref (G_OBJECT (folder));
730 tny_iterator_next (iter);
732 g_object_unref (G_OBJECT (iter));
733 g_object_unref (G_OBJECT (folders));
737 * Issues the "progress-changed" signal. The timer won't be removed,
738 * so you must call g_source_remove to stop the signal emission
741 idle_notify_progress (gpointer data)
743 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
744 ModestMailOperationState *state;
746 state = modest_mail_operation_clone_state (mail_op);
747 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
748 g_slice_free (ModestMailOperationState, state);
754 * Issues the "progress-changed" signal and removes the timer. It uses
755 * a lock to ensure that the progress information of the mail
756 * operation is not modified while there are notifications pending
759 idle_notify_progress_once (gpointer data)
763 pair = (ModestPair *) data;
765 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
767 /* Free the state and the reference to the mail operation */
768 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
769 g_object_unref (pair->first);
775 * Used by update_account_thread to notify the queue from the main
776 * loop. We call it inside an idle call to achieve that
779 notify_update_account_queue (gpointer data)
781 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
782 ModestMailOperationPrivate *priv = NULL;
784 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
786 modest_mail_operation_notify_end (mail_op);
787 g_object_unref (mail_op);
793 compare_headers_by_date (gconstpointer a,
796 TnyHeader **header1, **header2;
799 header1 = (TnyHeader **) a;
800 header2 = (TnyHeader **) b;
802 sent1 = tny_header_get_date_sent (*header1);
803 sent2 = tny_header_get_date_sent (*header2);
805 /* We want the most recent ones (greater time_t) at the
814 set_last_updated_idle (gpointer data)
816 /* It does not matter if the time is not exactly the same than
817 the time when this idle was called, it's just an
818 approximation and it won't be very different */
819 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
821 MODEST_ACCOUNT_LAST_UPDATED,
829 update_account_thread (gpointer thr_user_data)
831 UpdateAccountInfo *info;
832 TnyList *all_folders = NULL;
833 GPtrArray *new_headers;
834 TnyIterator *iter = NULL;
835 TnyFolderStoreQuery *query = NULL;
836 ModestMailOperationPrivate *priv;
837 ModestTnySendQueue *send_queue;
840 info = (UpdateAccountInfo *) thr_user_data;
841 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
843 /* Get account and set it into mail_operation */
844 priv->account = g_object_ref (info->account);
847 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
848 * show any updates unless we do that
850 if (TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
851 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
853 /* Get all the folders. We can do it synchronously because
854 we're already running in a different thread than the UI */
855 all_folders = tny_simple_list_new ();
856 query = tny_folder_store_query_new ();
857 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
858 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
863 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
867 iter = tny_list_create_iterator (all_folders);
868 while (!tny_iterator_is_done (iter)) {
869 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
871 recurse_folders (folder, query, all_folders);
872 tny_iterator_next (iter);
874 g_object_unref (G_OBJECT (iter));
876 /* Update status and notify. We need to call the notification
877 with a source function in order to call it from the main
878 loop. We need that in order not to get into trouble with
879 Gtk+. We use a timeout in order to provide more status
880 information, because the sync tinymail call does not
881 provide it for the moment */
882 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
884 /* Refresh folders */
885 new_headers = g_ptr_array_new ();
886 iter = tny_list_create_iterator (all_folders);
888 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
890 InternalFolderObserver *observer;
891 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
893 /* Refresh the folder */
894 /* Our observer receives notification of new emails during folder refreshes,
895 * so we can use observer->new_headers.
896 * TODO: This does not seem to be providing accurate numbers.
897 * Possibly the observer is notified asynchronously.
899 observer = g_object_new (internal_folder_observer_get_type (), NULL);
900 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
902 /* This gets the status information (headers) from the server.
903 * We use the blocking version, because we are already in a separate
907 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
908 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
911 /* If the retrieve type is full messages, refresh and get the messages */
912 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
914 iter = tny_list_create_iterator (observer->new_headers);
915 while (!tny_iterator_is_done (iter)) {
916 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
917 /* printf (" DEBUG1.2 %s: checking size: account=%s, subject=%s\n",
918 * __FUNCTION__, tny_account_get_id (priv->account),
919 * tny_header_get_subject (header));
922 /* Apply per-message size limits */
923 if (tny_header_get_message_size (header) < info->max_size)
924 g_ptr_array_add (new_headers, g_object_ref (header));
926 g_object_unref (header);
927 tny_iterator_next (iter);
929 g_object_unref (iter);
932 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
933 g_object_unref (observer);
937 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
939 g_object_unref (G_OBJECT (folder));
940 tny_iterator_next (iter);
943 did_a_cancel = FALSE;
945 g_object_unref (G_OBJECT (iter));
946 g_source_remove (timeout);
948 if (new_headers->len > 0) {
952 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
954 /* Apply message count limit */
955 /* If the number of messages exceeds the maximum, ask the
956 * user to download them all,
957 * as per the UI spec "Retrieval Limits" section in 4.4:
959 printf ("DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__,
960 tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
961 if (new_headers->len > info->retrieve_limit) {
962 /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded,
963 * with 'Get all' and 'Newest only' buttons. */
964 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
965 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
966 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
967 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
968 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
973 priv->total = MIN (new_headers->len, info->retrieve_limit);
974 while (msg_num < priv->total) {
976 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
977 TnyFolder *folder = tny_header_get_folder (header);
978 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
979 ModestMailOperationState *state;
983 /* We can not just use the mail operation because the
984 values of done and total could change before the
986 state = modest_mail_operation_clone_state (info->mail_op);
987 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
988 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
989 pair, (GDestroyNotify) modest_pair_free);
991 g_object_unref (msg);
992 g_object_unref (folder);
996 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
997 g_ptr_array_free (new_headers, FALSE);
1001 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1004 if (priv->account != NULL)
1005 g_object_unref (priv->account);
1006 priv->account = g_object_ref (info->transport_account);
1008 send_queue = modest_runtime_get_send_queue (info->transport_account);
1010 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1011 modest_tny_send_queue_try_to_send (send_queue);
1012 g_source_remove (timeout);
1014 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1015 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1016 "cannot create a send queue for %s\n",
1017 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1018 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1021 /* Check if the operation was a success */
1023 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1025 /* Update the last updated key */
1026 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1027 set_last_updated_idle,
1028 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1029 (GDestroyNotify) g_free);
1033 /* Notify about operation end. Note that the info could be
1034 freed before this idle happens, but the mail operation will
1036 g_idle_add (notify_update_account_queue, info->mail_op);
1039 g_object_unref (query);
1040 g_object_unref (all_folders);
1041 g_object_unref (info->account);
1042 g_object_unref (info->transport_account);
1043 g_free (info->retrieve_type);
1044 g_slice_free (UpdateAccountInfo, info);
1050 modest_mail_operation_update_account (ModestMailOperation *self,
1051 const gchar *account_name)
1054 UpdateAccountInfo *info;
1055 ModestMailOperationPrivate *priv;
1056 ModestAccountMgr *mgr;
1057 TnyStoreAccount *modest_account;
1058 TnyTransportAccount *transport_account;
1060 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1061 g_return_val_if_fail (account_name, FALSE);
1063 /* Make sure that we have a connection, and request one
1065 * TODO: Is there some way to trigger this for every attempt to
1066 * use the network? */
1067 if (!modest_platform_connect_and_wait(NULL))
1070 /* Init mail operation. Set total and done to 0, and do not
1071 update them, this way the progress objects will know that
1072 we have no clue about the number of the objects */
1073 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1076 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1078 /* Get the Modest account */
1079 modest_account = (TnyStoreAccount *)
1080 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1082 TNY_ACCOUNT_TYPE_STORE);
1084 if (!modest_account) {
1085 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1086 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1087 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1088 "cannot get tny store account for %s\n", account_name);
1089 modest_mail_operation_notify_end (self);
1095 /* Get the transport account, we can not do it in the thread
1096 due to some problems with dbus */
1097 transport_account = (TnyTransportAccount *)
1098 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1100 if (!transport_account) {
1101 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1102 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1103 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1104 "cannot get tny transport account for %s\n", account_name);
1105 modest_mail_operation_notify_end (self);
1110 /* Create the helper object */
1111 info = g_slice_new (UpdateAccountInfo);
1112 info->mail_op = self;
1113 info->account = modest_account;
1114 info->transport_account = transport_account;
1116 /* Get the message size limit */
1117 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1118 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1119 if (info->max_size == 0)
1120 info->max_size = G_MAXINT;
1122 info->max_size = info->max_size * KB;
1124 /* Get per-account retrieval type */
1125 mgr = modest_runtime_get_account_mgr ();
1126 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1127 MODEST_ACCOUNT_RETRIEVE, FALSE);
1129 /* Get per-account message amount retrieval limit */
1130 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1131 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1132 if (info->retrieve_limit == 0)
1133 info->retrieve_limit = G_MAXINT;
1135 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1137 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1142 /* ******************************************************************* */
1143 /* ************************** STORE ACTIONS ************************* */
1144 /* ******************************************************************* */
1148 modest_mail_operation_create_folder (ModestMailOperation *self,
1149 TnyFolderStore *parent,
1152 ModestMailOperationPrivate *priv;
1153 TnyFolder *new_folder = NULL;
1155 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1156 g_return_val_if_fail (name, NULL);
1158 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1161 if (TNY_IS_FOLDER (parent)) {
1162 /* Check folder rules */
1163 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1164 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1165 /* Set status failed and set an error */
1166 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1167 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1168 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1169 _("mail_in_ui_folder_create_error"));
1174 /* Create the folder */
1175 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1176 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1178 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1181 /* Notify about operation end */
1182 modest_mail_operation_notify_end (self);
1188 modest_mail_operation_remove_folder (ModestMailOperation *self,
1190 gboolean remove_to_trash)
1192 TnyAccount *account;
1193 ModestMailOperationPrivate *priv;
1194 ModestTnyFolderRules rules;
1196 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1197 g_return_if_fail (TNY_IS_FOLDER (folder));
1199 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1201 /* Check folder rules */
1202 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1203 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1204 /* Set status failed and set an error */
1205 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1206 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1207 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1208 _("mail_in_ui_folder_delete_error"));
1212 /* Get the account */
1213 account = modest_tny_folder_get_account (folder);
1214 priv->account = g_object_ref(account);
1216 /* Delete folder or move to trash */
1217 if (remove_to_trash) {
1218 TnyFolder *trash_folder = NULL;
1219 trash_folder = modest_tny_account_get_special_folder (account,
1220 TNY_FOLDER_TYPE_TRASH);
1221 /* TODO: error_handling */
1222 modest_mail_operation_xfer_folder (self, folder,
1223 TNY_FOLDER_STORE (trash_folder), TRUE);
1225 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1227 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1228 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1231 g_object_unref (G_OBJECT (parent));
1233 g_object_unref (G_OBJECT (account));
1236 /* Notify about operation end */
1237 modest_mail_operation_notify_end (self);
1241 transfer_folder_status_cb (GObject *obj,
1245 ModestMailOperation *self;
1246 ModestMailOperationPrivate *priv;
1247 ModestMailOperationState *state;
1249 g_return_if_fail (status != NULL);
1250 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1252 self = MODEST_MAIL_OPERATION (user_data);
1253 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1255 if ((status->position == 1) && (status->of_total == 100))
1258 priv->done = status->position;
1259 priv->total = status->of_total;
1261 state = modest_mail_operation_clone_state (self);
1262 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1263 g_slice_free (ModestMailOperationState, state);
1268 transfer_folder_cb (TnyFolder *folder,
1269 TnyFolderStore *into,
1271 TnyFolder *new_folder, GError **err,
1274 ModestMailOperation *self = NULL;
1275 ModestMailOperationPrivate *priv = NULL;
1277 self = MODEST_MAIL_OPERATION (user_data);
1279 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1282 priv->error = g_error_copy (*err);
1284 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1285 } else if (cancelled) {
1286 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1287 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1288 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1289 _("Transference of %s was cancelled."),
1290 tny_folder_get_name (folder));
1293 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1297 g_object_unref (folder);
1298 g_object_unref (into);
1299 if (new_folder != NULL)
1300 g_object_unref (new_folder);
1302 /* Notify about operation end */
1303 modest_mail_operation_notify_end (self);
1307 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1309 TnyFolderStore *parent,
1310 gboolean delete_original)
1312 ModestMailOperationPrivate *priv = NULL;
1313 ModestTnyFolderRules parent_rules, rules;
1315 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1316 g_return_if_fail (TNY_IS_FOLDER (folder));
1317 g_return_if_fail (TNY_IS_FOLDER (parent));
1319 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1321 /* Get account and set it into mail_operation */
1322 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1323 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1325 /* Get folder rules */
1326 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1327 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1329 if (!TNY_IS_FOLDER_STORE (parent)) {
1333 /* The moveable restriction is applied also to copy operation */
1334 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1335 /* Set status failed and set an error */
1336 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1337 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1338 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1339 _("mail_in_ui_folder_move_target_error"));
1341 /* Notify the queue */
1342 modest_mail_operation_notify_end (self);
1343 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1344 /* Set status failed and set an error */
1345 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1346 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1347 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1348 _("FIXME: parent folder does not accept new folders"));
1350 /* Notify the queue */
1351 modest_mail_operation_notify_end (self);
1353 /* Pick references for async calls */
1354 g_object_ref (folder);
1355 g_object_ref (parent);
1357 /* Move/Copy folder */
1358 tny_folder_copy_async (folder,
1360 tny_folder_get_name (folder),
1363 transfer_folder_status_cb,
1369 modest_mail_operation_rename_folder (ModestMailOperation *self,
1373 ModestMailOperationPrivate *priv;
1374 ModestTnyFolderRules rules;
1376 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1377 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1378 g_return_if_fail (name);
1380 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1382 /* Get account and set it into mail_operation */
1383 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1385 /* Check folder rules */
1386 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1387 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1388 /* Set status failed and set an error */
1389 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1390 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1391 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1392 _("FIXME: unable to rename"));
1394 /* Notify about operation end */
1395 modest_mail_operation_notify_end (self);
1397 /* Rename. Camel handles folder subscription/unsubscription */
1398 TnyFolderStore *into;
1400 into = tny_folder_get_folder_store (folder);
1401 tny_folder_copy_async (folder, into, name, TRUE,
1403 transfer_folder_status_cb,
1406 g_object_unref (into);
1411 /* ******************************************************************* */
1412 /* ************************** MSG ACTIONS ************************* */
1413 /* ******************************************************************* */
1415 void modest_mail_operation_get_msg (ModestMailOperation *self,
1417 GetMsgAsyncUserCallback user_callback,
1420 GetMsgAsyncHelper *helper = NULL;
1422 ModestMailOperationPrivate *priv;
1424 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1425 g_return_if_fail (TNY_IS_HEADER (header));
1427 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1428 folder = tny_header_get_folder (header);
1430 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1432 /* Get message from folder */
1434 /* Get account and set it into mail_operation */
1435 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1437 helper = g_slice_new0 (GetMsgAsyncHelper);
1438 helper->mail_op = self;
1439 helper->user_callback = user_callback;
1440 helper->pending_ops = 1;
1441 helper->user_data = user_data;
1443 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1445 g_object_unref (G_OBJECT (folder));
1447 /* Set status failed and set an error */
1448 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1449 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1450 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1451 _("Error trying to get a message. No folder found for header"));
1453 /* Notify the queue */
1454 modest_mail_operation_notify_end (self);
1459 get_msg_cb (TnyFolder *folder,
1465 GetMsgAsyncHelper *helper = NULL;
1466 ModestMailOperation *self = NULL;
1467 ModestMailOperationPrivate *priv = NULL;
1469 helper = (GetMsgAsyncHelper *) user_data;
1470 g_return_if_fail (helper != NULL);
1471 self = helper->mail_op;
1472 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1473 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1475 helper->pending_ops--;
1477 /* Check errors and cancel */
1479 priv->error = g_error_copy (*error);
1480 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1484 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1485 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1486 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1487 _("Error trying to refresh the contents of %s"),
1488 tny_folder_get_name (folder));
1492 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1494 /* If user defined callback function was defined, call it */
1495 if (helper->user_callback) {
1496 helper->user_callback (self, NULL, msg, helper->user_data);
1501 if (helper->pending_ops == 0) {
1502 g_slice_free (GetMsgAsyncHelper, helper);
1504 /* Notify about operation end */
1505 modest_mail_operation_notify_end (self);
1510 get_msg_status_cb (GObject *obj,
1514 GetMsgAsyncHelper *helper = NULL;
1515 ModestMailOperation *self;
1516 ModestMailOperationPrivate *priv;
1517 ModestMailOperationState *state;
1519 g_return_if_fail (status != NULL);
1520 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1522 helper = (GetMsgAsyncHelper *) user_data;
1523 g_return_if_fail (helper != NULL);
1525 self = helper->mail_op;
1526 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1528 if ((status->position == 1) && (status->of_total == 100))
1534 state = modest_mail_operation_clone_state (self);
1535 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1536 g_slice_free (ModestMailOperationState, state);
1539 /****************************************************/
1541 ModestMailOperation *mail_op;
1543 GetMsgAsyncUserCallback user_callback;
1545 GDestroyNotify notify;
1549 GetMsgAsyncUserCallback user_callback;
1553 ModestMailOperation *mail_op;
1554 } NotifyGetMsgsInfo;
1558 * Used by get_msgs_full_thread to call the user_callback for each
1559 * message that has been read
1562 notify_get_msgs_full (gpointer data)
1564 NotifyGetMsgsInfo *info;
1566 info = (NotifyGetMsgsInfo *) data;
1568 /* Call the user callback */
1569 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1571 g_slice_free (NotifyGetMsgsInfo, info);
1577 * Used by get_msgs_full_thread to free al the thread resources and to
1578 * call the destroy function for the passed user_data
1581 get_msgs_full_destroyer (gpointer data)
1583 GetFullMsgsInfo *info;
1585 info = (GetFullMsgsInfo *) data;
1588 info->notify (info->user_data);
1591 g_object_unref (info->headers);
1592 g_slice_free (GetFullMsgsInfo, info);
1598 get_msgs_full_thread (gpointer thr_user_data)
1600 GetFullMsgsInfo *info;
1601 ModestMailOperationPrivate *priv = NULL;
1602 TnyIterator *iter = NULL;
1604 info = (GetFullMsgsInfo *) thr_user_data;
1605 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1607 iter = tny_list_create_iterator (info->headers);
1608 while (!tny_iterator_is_done (iter)) {
1612 header = TNY_HEADER (tny_iterator_get_current (iter));
1613 folder = tny_header_get_folder (header);
1615 /* Get message from folder */
1618 /* The callback will call it per each header */
1619 msg = tny_folder_get_msg (folder, header, &(priv->error));
1622 ModestMailOperationState *state;
1627 /* notify progress */
1628 state = modest_mail_operation_clone_state (info->mail_op);
1629 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1630 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1631 pair, (GDestroyNotify) modest_pair_free);
1633 /* The callback is the responsible for
1634 freeing the message */
1635 if (info->user_callback) {
1636 NotifyGetMsgsInfo *info_notify;
1637 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1638 info_notify->user_callback = info->user_callback;
1639 info_notify->mail_op = info->mail_op;
1640 info_notify->header = g_object_ref (header);
1641 info_notify->msg = g_object_ref (msg);
1642 info_notify->user_data = info->user_data;
1643 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1644 notify_get_msgs_full,
1647 g_object_unref (msg);
1650 /* Set status failed and set an error */
1651 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1652 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1653 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1654 "Error trying to get a message. No folder found for header");
1656 g_object_unref (header);
1657 tny_iterator_next (iter);
1660 /* Set operation status */
1661 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1662 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1664 /* Notify about operation end */
1665 g_idle_add (notify_update_account_queue, info->mail_op);
1667 /* Free thread resources. Will be called after all previous idles */
1668 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1674 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1675 TnyList *header_list,
1676 GetMsgAsyncUserCallback user_callback,
1678 GDestroyNotify notify)
1680 TnyHeader *header = NULL;
1681 TnyFolder *folder = NULL;
1683 ModestMailOperationPrivate *priv = NULL;
1684 GetFullMsgsInfo *info = NULL;
1685 gboolean size_ok = TRUE;
1687 TnyIterator *iter = NULL;
1689 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1691 /* Init mail operation */
1692 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1693 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1695 priv->total = tny_list_get_length(header_list);
1697 /* Get account and set it into mail_operation */
1698 if (tny_list_get_length (header_list) >= 1) {
1699 iter = tny_list_create_iterator (header_list);
1700 header = TNY_HEADER (tny_iterator_get_current (iter));
1701 folder = tny_header_get_folder (header);
1702 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1703 g_object_unref (header);
1704 g_object_unref (folder);
1706 if (tny_list_get_length (header_list) == 1) {
1707 g_object_unref (iter);
1712 /* Get msg size limit */
1713 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1714 MODEST_CONF_MSG_SIZE_LIMIT,
1717 g_clear_error (&(priv->error));
1718 max_size = G_MAXINT;
1720 max_size = max_size * KB;
1723 /* Check message size limits. If there is only one message
1724 always retrieve it */
1726 while (!tny_iterator_is_done (iter) && size_ok) {
1727 header = TNY_HEADER (tny_iterator_get_current (iter));
1728 if (tny_header_get_message_size (header) >= max_size)
1730 g_object_unref (header);
1731 tny_iterator_next (iter);
1733 g_object_unref (iter);
1737 /* Create the info */
1738 info = g_slice_new0 (GetFullMsgsInfo);
1739 info->mail_op = self;
1740 info->user_callback = user_callback;
1741 info->user_data = user_data;
1742 info->headers = g_object_ref (header_list);
1743 info->notify = notify;
1745 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1747 /* Set status failed and set an error */
1748 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1749 /* FIXME: the error msg is different for pop */
1750 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1751 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1752 _("emev_ni_ui_imap_msg_size_exceed_error"));
1753 /* Remove from queue and free resources */
1754 modest_mail_operation_notify_end (self);
1762 modest_mail_operation_remove_msg (ModestMailOperation *self,
1764 gboolean remove_to_trash)
1767 ModestMailOperationPrivate *priv;
1769 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1770 g_return_if_fail (TNY_IS_HEADER (header));
1772 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1773 folder = tny_header_get_folder (header);
1775 /* Get account and set it into mail_operation */
1776 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1778 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1780 /* Delete or move to trash */
1781 if (remove_to_trash) {
1782 TnyFolder *trash_folder;
1783 TnyStoreAccount *store_account;
1785 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1786 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1787 TNY_FOLDER_TYPE_TRASH);
1792 headers = tny_simple_list_new ();
1793 tny_list_append (headers, G_OBJECT (header));
1794 g_object_unref (header);
1797 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1798 g_object_unref (headers);
1799 /* g_object_unref (trash_folder); */
1801 ModestMailOperationPrivate *priv;
1803 /* Set status failed and set an error */
1804 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1805 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1806 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1807 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1808 _("Error trying to delete a message. Trash folder not found"));
1811 g_object_unref (G_OBJECT (store_account));
1813 tny_folder_remove_msg (folder, header, &(priv->error));
1815 tny_folder_sync(folder, TRUE, &(priv->error));
1820 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1822 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1825 g_object_unref (G_OBJECT (folder));
1827 /* Notify about operation end */
1828 modest_mail_operation_notify_end (self);
1832 transfer_msgs_status_cb (GObject *obj,
1836 XFerMsgAsyncHelper *helper = NULL;
1837 ModestMailOperation *self;
1838 ModestMailOperationPrivate *priv;
1839 ModestMailOperationState *state;
1842 g_return_if_fail (status != NULL);
1843 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1845 helper = (XFerMsgAsyncHelper *) user_data;
1846 g_return_if_fail (helper != NULL);
1848 self = helper->mail_op;
1849 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1851 if ((status->position == 1) && (status->of_total == 100))
1854 priv->done = status->position;
1855 priv->total = status->of_total;
1857 state = modest_mail_operation_clone_state (self);
1858 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1859 g_slice_free (ModestMailOperationState, state);
1864 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1866 XFerMsgAsyncHelper *helper;
1867 ModestMailOperation *self;
1868 ModestMailOperationPrivate *priv;
1870 helper = (XFerMsgAsyncHelper *) user_data;
1871 self = helper->mail_op;
1873 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1876 priv->error = g_error_copy (*err);
1878 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1879 } else if (cancelled) {
1880 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1881 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1882 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1883 _("Error trying to refresh the contents of %s"),
1884 tny_folder_get_name (folder));
1887 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1890 /* If user defined callback function was defined, call it */
1891 if (helper->user_callback) {
1892 helper->user_callback (priv->source, helper->user_data);
1896 g_object_unref (helper->headers);
1897 g_object_unref (helper->dest_folder);
1898 g_object_unref (helper->mail_op);
1899 g_slice_free (XFerMsgAsyncHelper, helper);
1900 g_object_unref (folder);
1902 /* Notify about operation end */
1903 modest_mail_operation_notify_end (self);
1907 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1910 gboolean delete_original,
1911 XferMsgsAsynUserCallback user_callback,
1914 ModestMailOperationPrivate *priv;
1916 TnyFolder *src_folder;
1917 XFerMsgAsyncHelper *helper;
1919 ModestTnyFolderRules rules;
1921 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1922 g_return_if_fail (TNY_IS_LIST (headers));
1923 g_return_if_fail (TNY_IS_FOLDER (folder));
1925 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1928 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1930 /* Apply folder rules */
1931 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1933 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1934 /* Set status failed and set an error */
1935 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1936 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1937 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1938 _("FIXME: folder does not accept msgs"));
1939 /* Notify the queue */
1940 modest_mail_operation_notify_end (self);
1944 /* Create the helper */
1945 helper = g_slice_new0 (XFerMsgAsyncHelper);
1946 helper->mail_op = g_object_ref(self);
1947 helper->dest_folder = g_object_ref(folder);
1948 helper->headers = g_object_ref(headers);
1949 helper->user_callback = user_callback;
1950 helper->user_data = user_data;
1952 /* Get source folder */
1953 iter = tny_list_create_iterator (headers);
1954 header = TNY_HEADER (tny_iterator_get_current (iter));
1955 src_folder = tny_header_get_folder (header);
1956 g_object_unref (header);
1957 g_object_unref (iter);
1959 /* Get account and set it into mail_operation */
1960 priv->account = modest_tny_folder_get_account (src_folder);
1962 /* Transfer messages */
1963 tny_folder_transfer_msgs_async (src_folder,
1968 transfer_msgs_status_cb,
1974 on_refresh_folder (TnyFolder *folder,
1979 ModestMailOperation *self;
1980 ModestMailOperationPrivate *priv;
1982 self = MODEST_MAIL_OPERATION (user_data);
1983 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1986 priv->error = g_error_copy (*error);
1987 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1992 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1993 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1994 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1995 _("Error trying to refresh the contents of %s"),
1996 tny_folder_get_name (folder));
2000 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2004 g_object_unref (folder);
2006 /* Notify about operation end */
2007 modest_mail_operation_notify_end (self);
2011 on_refresh_folder_status_update (GObject *obj,
2015 ModestMailOperation *self;
2016 ModestMailOperationPrivate *priv;
2017 ModestMailOperationState *state;
2019 g_return_if_fail (status != NULL);
2020 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2022 self = MODEST_MAIL_OPERATION (user_data);
2023 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2025 priv->done = status->position;
2026 priv->total = status->of_total;
2028 state = modest_mail_operation_clone_state (self);
2029 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2030 g_slice_free (ModestMailOperationState, state);
2034 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2037 ModestMailOperationPrivate *priv;
2039 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2041 /* Pick a reference */
2042 g_object_ref (folder);
2044 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2046 /* Get account and set it into mail_operation */
2047 priv->account = modest_tny_folder_get_account (folder);
2049 /* Refresh the folder. TODO: tinymail could issue a status
2050 updates before the callback call then this could happen. We
2051 must review the design */
2052 tny_folder_refresh_async (folder,
2054 on_refresh_folder_status_update,
2060 * It's used by the mail operation queue to notify the observers
2061 * attached to that signal that the operation finished. We need to use
2062 * that because tinymail does not give us the progress of a given
2063 * operation when it finishes (it directly calls the operation
2067 modest_mail_operation_notify_end (ModestMailOperation *self)
2069 ModestMailOperationState *state;
2071 /* Notify the observers about the mail opertation end */
2072 state = modest_mail_operation_clone_state (self);
2073 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2074 g_slice_free (ModestMailOperationState, state);