1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <tny-mime-part.h>
33 #include <tny-store-account.h>
34 #include <tny-folder-store.h>
35 #include <tny-folder-store-query.h>
36 #include <tny-camel-stream.h>
37 #include <tny-camel-pop-store-account.h>
38 #include <tny-simple-list.h>
39 #include <tny-send-queue.h>
40 #include <tny-status.h>
41 #include <tny-folder-observer.h>
42 #include <camel/camel-stream-mem.h>
43 #include <glib/gi18n.h>
44 #include "modest-platform.h"
45 #include <modest-tny-account.h>
46 #include <modest-tny-send-queue.h>
47 #include <modest-runtime.h>
48 #include "modest-text-utils.h"
49 #include "modest-tny-msg.h"
50 #include "modest-tny-folder.h"
51 #include "modest-tny-platform-factory.h"
52 #include "modest-marshal.h"
53 #include "modest-error.h"
54 #include "modest-mail-operation.h"
58 /* 'private'/'protected' functions */
59 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
60 static void modest_mail_operation_init (ModestMailOperation *obj);
61 static void modest_mail_operation_finalize (GObject *obj);
63 static void get_msg_cb (TnyFolder *folder,
69 static void get_msg_status_cb (GObject *obj,
73 static void modest_mail_operation_notify_end (ModestMailOperation *self);
75 static gboolean did_a_cancel = FALSE;
77 enum _ModestMailOperationSignals
79 PROGRESS_CHANGED_SIGNAL,
84 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
85 struct _ModestMailOperationPrivate {
92 ErrorCheckingUserCallback error_checking;
93 gpointer error_checking_user_data;
94 ModestMailOperationStatus status;
95 ModestMailOperationTypeOperation op_type;
98 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
99 MODEST_TYPE_MAIL_OPERATION, \
100 ModestMailOperationPrivate))
102 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
103 priv->status = new_status;\
106 typedef struct _GetMsgAsyncHelper {
107 ModestMailOperation *mail_op;
109 GetMsgAsyncUserCallback user_callback;
113 typedef struct _RefreshAsyncHelper {
114 ModestMailOperation *mail_op;
115 RefreshAsyncUserCallback user_callback;
117 } RefreshAsyncHelper;
119 typedef struct _XFerMsgAsyncHelper
121 ModestMailOperation *mail_op;
123 TnyFolder *dest_folder;
124 XferMsgsAsynUserCallback user_callback;
126 } XFerMsgAsyncHelper;
129 static GObjectClass *parent_class = NULL;
131 static guint signals[NUM_SIGNALS] = {0};
134 modest_mail_operation_get_type (void)
136 static GType my_type = 0;
138 static const GTypeInfo my_info = {
139 sizeof(ModestMailOperationClass),
140 NULL, /* base init */
141 NULL, /* base finalize */
142 (GClassInitFunc) modest_mail_operation_class_init,
143 NULL, /* class finalize */
144 NULL, /* class data */
145 sizeof(ModestMailOperation),
147 (GInstanceInitFunc) modest_mail_operation_init,
150 my_type = g_type_register_static (G_TYPE_OBJECT,
151 "ModestMailOperation",
158 modest_mail_operation_class_init (ModestMailOperationClass *klass)
160 GObjectClass *gobject_class;
161 gobject_class = (GObjectClass*) klass;
163 parent_class = g_type_class_peek_parent (klass);
164 gobject_class->finalize = modest_mail_operation_finalize;
166 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
169 * ModestMailOperation::progress-changed
170 * @self: the #MailOperation that emits the signal
171 * @user_data: user data set when the signal handler was connected
173 * Emitted when the progress of a mail operation changes
175 signals[PROGRESS_CHANGED_SIGNAL] =
176 g_signal_new ("progress-changed",
177 G_TYPE_FROM_CLASS (gobject_class),
179 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
181 g_cclosure_marshal_VOID__POINTER,
182 G_TYPE_NONE, 1, G_TYPE_POINTER);
187 modest_mail_operation_init (ModestMailOperation *obj)
189 ModestMailOperationPrivate *priv;
191 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
193 priv->account = NULL;
194 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
195 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
200 priv->error_checking = NULL;
201 priv->error_checking_user_data = NULL;
205 modest_mail_operation_finalize (GObject *obj)
207 ModestMailOperationPrivate *priv;
209 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
214 g_error_free (priv->error);
218 g_object_unref (priv->source);
222 g_object_unref (priv->account);
223 priv->account = NULL;
227 G_OBJECT_CLASS(parent_class)->finalize (obj);
231 modest_mail_operation_new (ModestMailOperationTypeOperation op_type,
234 ModestMailOperation *obj;
235 ModestMailOperationPrivate *priv;
237 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
238 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
240 priv->op_type = op_type;
242 priv->source = g_object_ref(source);
248 modest_mail_operation_new_with_error_handling (ModestMailOperationTypeOperation op_type,
250 ErrorCheckingUserCallback error_handler,
253 ModestMailOperation *obj;
254 ModestMailOperationPrivate *priv;
256 obj = modest_mail_operation_new (op_type, source);
257 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
259 g_return_val_if_fail (error_handler != NULL, obj);
260 priv->error_checking = error_handler;
266 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
268 ModestMailOperationPrivate *priv;
270 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
271 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
273 if (priv->error_checking != NULL)
274 priv->error_checking (self, priv->error_checking_user_data);
278 ModestMailOperationTypeOperation
279 modest_mail_operation_get_type_operation (ModestMailOperation *self)
281 ModestMailOperationPrivate *priv;
283 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
285 return priv->op_type;
289 modest_mail_operation_is_mine (ModestMailOperation *self,
292 ModestMailOperationPrivate *priv;
294 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
295 if (priv->source == NULL) return FALSE;
297 return priv->source == me;
301 modest_mail_operation_get_source (ModestMailOperation *self)
303 ModestMailOperationPrivate *priv;
305 g_return_val_if_fail (self, NULL);
307 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
309 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
313 return g_object_ref (priv->source);
316 ModestMailOperationStatus
317 modest_mail_operation_get_status (ModestMailOperation *self)
319 ModestMailOperationPrivate *priv;
321 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
322 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
323 MODEST_MAIL_OPERATION_STATUS_INVALID);
325 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
327 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
328 return MODEST_MAIL_OPERATION_STATUS_INVALID;
335 modest_mail_operation_get_error (ModestMailOperation *self)
337 ModestMailOperationPrivate *priv;
339 g_return_val_if_fail (self, NULL);
340 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
342 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
345 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
353 modest_mail_operation_cancel (ModestMailOperation *self)
355 ModestMailOperationPrivate *priv;
357 if (!MODEST_IS_MAIL_OPERATION (self)) {
358 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
362 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
364 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
371 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
373 /* This emits progress-changed on which the mail operation queue is
374 * listening, so the mail operation is correctly removed from the
375 * queue without further explicit calls. */
376 modest_mail_operation_notify_end (self);
382 modest_mail_operation_get_task_done (ModestMailOperation *self)
384 ModestMailOperationPrivate *priv;
386 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
388 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
393 modest_mail_operation_get_task_total (ModestMailOperation *self)
395 ModestMailOperationPrivate *priv;
397 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
399 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
404 modest_mail_operation_is_finished (ModestMailOperation *self)
406 ModestMailOperationPrivate *priv;
407 gboolean retval = FALSE;
409 if (!MODEST_IS_MAIL_OPERATION (self)) {
410 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
414 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
416 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
417 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
418 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
419 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
429 modest_mail_operation_get_id (ModestMailOperation *self)
431 ModestMailOperationPrivate *priv;
433 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
435 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
440 modest_mail_operation_set_id (ModestMailOperation *self,
443 ModestMailOperationPrivate *priv;
445 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
447 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
452 * Creates an image of the current state of a mail operation, the
453 * caller must free it
455 static ModestMailOperationState *
456 modest_mail_operation_clone_state (ModestMailOperation *self)
458 ModestMailOperationState *state;
459 ModestMailOperationPrivate *priv;
461 /* FIXME: this should be fixed properly
463 * in some cases, priv was NULL, so checking here to
466 g_return_val_if_fail (self, NULL);
467 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
468 g_return_val_if_fail (priv, NULL);
473 state = g_slice_new (ModestMailOperationState);
475 state->status = priv->status;
476 state->op_type = priv->op_type;
477 state->done = priv->done;
478 state->total = priv->total;
479 state->finished = modest_mail_operation_is_finished (self);
480 state->bytes_done = 0;
481 state->bytes_total = 0;
486 /* ******************************************************************* */
487 /* ************************** SEND ACTIONS ************************* */
488 /* ******************************************************************* */
491 modest_mail_operation_send_mail (ModestMailOperation *self,
492 TnyTransportAccount *transport_account,
495 TnySendQueue *send_queue = NULL;
496 ModestMailOperationPrivate *priv;
498 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
499 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
500 g_return_if_fail (TNY_IS_MSG (msg));
502 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
504 /* Get account and set it into mail_operation */
505 priv->account = g_object_ref (transport_account);
509 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
510 if (!TNY_IS_SEND_QUEUE(send_queue)) {
511 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
512 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
513 "modest: could not find send queue for account\n");
515 /* TODO: connect to the msg-sent in order to know when
516 the mail operation is finished */
517 tny_send_queue_add (send_queue, msg, &(priv->error));
518 /* TODO: we're setting always success, do the check in
520 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
523 /* TODO: do this in the handler of the "msg-sent"
524 signal.Notify about operation end */
525 modest_mail_operation_notify_end (self);
529 modest_mail_operation_send_new_mail (ModestMailOperation *self,
530 TnyTransportAccount *transport_account,
532 const gchar *from, const gchar *to,
533 const gchar *cc, const gchar *bcc,
534 const gchar *subject, const gchar *plain_body,
535 const gchar *html_body,
536 const GList *attachments_list,
537 TnyHeaderFlags priority_flags)
539 TnyMsg *new_msg = NULL;
540 TnyFolder *folder = NULL;
541 TnyHeader *header = NULL;
542 ModestMailOperationPrivate *priv = NULL;
544 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
545 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
547 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
549 /* Check parametters */
551 /* Set status failed and set an error */
552 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
553 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
554 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
555 _("Error trying to send a mail. You need to set at least one recipient"));
559 if (html_body == NULL) {
560 new_msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
562 new_msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
565 g_printerr ("modest: failed to create a new msg\n");
569 /* Set priority flags in message */
570 header = tny_msg_get_header (new_msg);
571 if (priority_flags != 0)
572 tny_header_set_flags (header, priority_flags);
574 /* Call mail operation */
575 modest_mail_operation_send_mail (self, transport_account, new_msg);
577 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
579 if (draft_msg != NULL) {
580 header = tny_msg_get_header (draft_msg);
581 /* Note: This can fail (with a warning) if the message is not really already in a folder,
582 * because this function requires it to have a UID. */
583 tny_folder_remove_msg (folder, header, NULL);
584 g_object_unref (header);
589 g_object_unref (G_OBJECT (new_msg));
593 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
594 TnyTransportAccount *transport_account,
596 const gchar *from, const gchar *to,
597 const gchar *cc, const gchar *bcc,
598 const gchar *subject, const gchar *plain_body,
599 const gchar *html_body,
600 const GList *attachments_list,
601 TnyHeaderFlags priority_flags)
604 TnyFolder *folder = NULL;
605 TnyHeader *header = NULL;
606 ModestMailOperationPrivate *priv = NULL;
608 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
609 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
611 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
613 /* Get account and set it into mail_operation */
614 priv->account = g_object_ref (transport_account);
616 if (html_body == NULL) {
617 msg = modest_tny_msg_new (to, from, cc, bcc, subject, plain_body, (GSList *) attachments_list); /* FIXME: attachments */
619 msg = modest_tny_msg_new_html_plain (to, from, cc, bcc, subject, html_body, plain_body, (GSList *) attachments_list);
622 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
623 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
624 "modest: failed to create a new msg\n");
628 /* add priority flags */
629 header = tny_msg_get_header (msg);
630 tny_header_set_flags (header, priority_flags);
632 folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (transport_account), TNY_FOLDER_TYPE_DRAFTS);
634 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
635 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
636 "modest: failed to create a new msg\n");
640 if (draft_msg != NULL) {
641 header = tny_msg_get_header (draft_msg);
642 /* Remove the old draft expunging it */
643 tny_folder_remove_msg (folder, header, NULL);
644 tny_folder_sync (folder, TRUE, NULL);
645 g_object_unref (header);
648 tny_folder_add_msg (folder, msg, &(priv->error));
654 g_object_unref (G_OBJECT(msg));
656 g_object_unref (G_OBJECT(folder));
658 modest_mail_operation_notify_end (self);
663 ModestMailOperation *mail_op;
664 TnyStoreAccount *account;
665 TnyTransportAccount *transport_account;
668 gchar *retrieve_type;
672 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
673 /* We use this folder observer to track the headers that have been
674 * added to a folder */
677 TnyList *new_headers;
678 } InternalFolderObserver;
682 } InternalFolderObserverClass;
684 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
686 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
687 internal_folder_observer,
689 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
693 foreach_add_item (gpointer header, gpointer user_data)
695 /* printf("DEBUG: %s: header subject=%s\n",
696 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
698 tny_list_prepend (TNY_LIST (user_data),
699 g_object_ref (G_OBJECT (header)));
702 /* This is the method that looks for new messages in a folder */
704 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
706 InternalFolderObserver *derived = (InternalFolderObserver *)self;
708 TnyFolderChangeChanged changed;
710 changed = tny_folder_change_get_changed (change);
712 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
715 /* Get added headers */
716 list = tny_simple_list_new ();
717 tny_folder_change_get_added_headers (change, list);
719 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
720 * __FUNCTION__, tny_list_get_length(list));
723 /* Add them to the folder observer */
724 tny_list_foreach (list, foreach_add_item,
725 derived->new_headers);
727 g_object_unref (G_OBJECT (list));
732 internal_folder_observer_init (InternalFolderObserver *self)
734 self->new_headers = tny_simple_list_new ();
737 internal_folder_observer_finalize (GObject *object)
739 InternalFolderObserver *self;
741 self = (InternalFolderObserver *) object;
742 g_object_unref (self->new_headers);
744 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
747 tny_folder_observer_init (TnyFolderObserverIface *iface)
749 iface->update_func = internal_folder_observer_update;
752 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
754 GObjectClass *object_class;
756 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
757 object_class = (GObjectClass*) klass;
758 object_class->finalize = internal_folder_observer_finalize;
764 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
767 TnyList *folders = tny_simple_list_new ();
769 tny_folder_store_get_folders (store, folders, query, NULL);
770 iter = tny_list_create_iterator (folders);
772 while (!tny_iterator_is_done (iter)) {
774 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
776 tny_list_prepend (all_folders, G_OBJECT (folder));
777 recurse_folders (folder, query, all_folders);
778 g_object_unref (G_OBJECT (folder));
780 tny_iterator_next (iter);
782 g_object_unref (G_OBJECT (iter));
783 g_object_unref (G_OBJECT (folders));
787 * Issues the "progress-changed" signal. The timer won't be removed,
788 * so you must call g_source_remove to stop the signal emission
791 idle_notify_progress (gpointer data)
793 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
794 ModestMailOperationState *state;
796 state = modest_mail_operation_clone_state (mail_op);
797 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
798 g_slice_free (ModestMailOperationState, state);
804 * Issues the "progress-changed" signal and removes the timer. It uses
805 * a lock to ensure that the progress information of the mail
806 * operation is not modified while there are notifications pending
809 idle_notify_progress_once (gpointer data)
813 pair = (ModestPair *) data;
815 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
817 /* Free the state and the reference to the mail operation */
818 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
819 g_object_unref (pair->first);
825 * Used by update_account_thread to notify the queue from the main
826 * loop. We call it inside an idle call to achieve that
829 notify_update_account_queue (gpointer data)
831 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
832 ModestMailOperationPrivate *priv = NULL;
834 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
836 modest_mail_operation_notify_end (mail_op);
837 g_object_unref (mail_op);
843 compare_headers_by_date (gconstpointer a,
846 TnyHeader **header1, **header2;
849 header1 = (TnyHeader **) a;
850 header2 = (TnyHeader **) b;
852 sent1 = tny_header_get_date_sent (*header1);
853 sent2 = tny_header_get_date_sent (*header2);
855 /* We want the most recent ones (greater time_t) at the
864 set_last_updated_idle (gpointer data)
866 /* It does not matter if the time is not exactly the same than
867 the time when this idle was called, it's just an
868 approximation and it won't be very different */
869 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
871 MODEST_ACCOUNT_LAST_UPDATED,
879 update_account_thread (gpointer thr_user_data)
881 static gboolean first_time = TRUE;
882 UpdateAccountInfo *info;
883 TnyList *all_folders = NULL;
884 GPtrArray *new_headers;
885 TnyIterator *iter = NULL;
886 TnyFolderStoreQuery *query = NULL;
887 ModestMailOperationPrivate *priv;
888 ModestTnySendQueue *send_queue;
890 info = (UpdateAccountInfo *) thr_user_data;
891 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
893 /* Get account and set it into mail_operation */
894 priv->account = g_object_ref (info->account);
897 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
898 * show any updates unless we do that
900 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
901 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
903 /* Get all the folders. We can do it synchronously because
904 we're already running in a different thread than the UI */
905 all_folders = tny_simple_list_new ();
906 query = tny_folder_store_query_new ();
907 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
908 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
913 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
917 iter = tny_list_create_iterator (all_folders);
918 while (!tny_iterator_is_done (iter)) {
919 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
921 recurse_folders (folder, query, all_folders);
922 tny_iterator_next (iter);
924 g_object_unref (G_OBJECT (iter));
926 /* Update status and notify. We need to call the notification
927 with a source function in order to call it from the main
928 loop. We need that in order not to get into trouble with
929 Gtk+. We use a timeout in order to provide more status
930 information, because the sync tinymail call does not
931 provide it for the moment */
932 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
934 /* Refresh folders */
935 new_headers = g_ptr_array_new ();
936 iter = tny_list_create_iterator (all_folders);
938 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
940 InternalFolderObserver *observer;
941 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
943 /* Refresh the folder */
944 /* Our observer receives notification of new emails during folder refreshes,
945 * so we can use observer->new_headers.
946 * TODO: This does not seem to be providing accurate numbers.
947 * Possibly the observer is notified asynchronously.
949 observer = g_object_new (internal_folder_observer_get_type (), NULL);
950 tny_folder_add_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
952 /* This gets the status information (headers) from the server.
953 * We use the blocking version, because we are already in a separate
957 if (!g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES) ||
958 !g_ascii_strcasecmp (info->retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_MESSAGES_AND_ATTACHMENTS)) {
961 /* If the retrieve type is full messages, refresh and get the messages */
962 tny_folder_refresh (TNY_FOLDER (folder), &(priv->error));
964 iter = tny_list_create_iterator (observer->new_headers);
965 while (!tny_iterator_is_done (iter)) {
966 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
968 /* Apply per-message size limits */
969 if (tny_header_get_message_size (header) < info->max_size)
970 g_ptr_array_add (new_headers, g_object_ref (header));
972 g_object_unref (header);
973 tny_iterator_next (iter);
975 g_object_unref (iter);
977 /* We do not need to do it the first time
978 because it's automatically done by the tree
980 if (G_UNLIKELY (!first_time))
981 tny_folder_poke_status (TNY_FOLDER (folder));
983 tny_folder_remove_observer (TNY_FOLDER (folder), TNY_FOLDER_OBSERVER (observer));
984 g_object_unref (observer);
988 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
990 g_object_unref (G_OBJECT (folder));
991 tny_iterator_next (iter);
994 did_a_cancel = FALSE;
996 g_object_unref (G_OBJECT (iter));
997 g_source_remove (timeout);
999 if (new_headers->len > 0) {
1003 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1005 /* Apply message count limit */
1006 /* If the number of messages exceeds the maximum, ask the
1007 * user to download them all,
1008 * as per the UI spec "Retrieval Limits" section in 4.4:
1010 printf ("************************** DEBUG: %s: account=%s, len=%d, retrieve_limit = %d\n", __FUNCTION__,
1011 tny_account_get_id (priv->account), new_headers->len, info->retrieve_limit);
1012 if (new_headers->len > info->retrieve_limit) {
1013 /* TODO: Ask the user, instead of just failing, showing mail_nc_msg_count_limit_exceeded,
1014 * with 'Get all' and 'Newest only' buttons. */
1015 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1016 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1017 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1018 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1019 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1024 priv->total = MIN (new_headers->len, info->retrieve_limit);
1025 while (msg_num < priv->total) {
1027 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1028 TnyFolder *folder = tny_header_get_folder (header);
1029 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1030 ModestMailOperationState *state;
1034 /* We can not just use the mail operation because the
1035 values of done and total could change before the
1037 state = modest_mail_operation_clone_state (info->mail_op);
1038 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1039 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1040 pair, (GDestroyNotify) modest_pair_free);
1042 g_object_unref (msg);
1043 g_object_unref (folder);
1047 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1048 g_ptr_array_free (new_headers, FALSE);
1052 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1055 if (priv->account != NULL)
1056 g_object_unref (priv->account);
1057 priv->account = g_object_ref (info->transport_account);
1059 send_queue = modest_runtime_get_send_queue (info->transport_account);
1061 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1062 modest_tny_send_queue_try_to_send (send_queue);
1063 g_source_remove (timeout);
1065 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1066 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1067 "cannot create a send queue for %s\n",
1068 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1069 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1072 /* Check if the operation was a success */
1074 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1076 /* Update the last updated key */
1077 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1078 set_last_updated_idle,
1079 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1080 (GDestroyNotify) g_free);
1084 /* Notify about operation end. Note that the info could be
1085 freed before this idle happens, but the mail operation will
1087 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1090 g_object_unref (query);
1091 g_object_unref (all_folders);
1092 g_object_unref (info->account);
1093 g_object_unref (info->transport_account);
1094 g_free (info->retrieve_type);
1095 g_slice_free (UpdateAccountInfo, info);
1103 modest_mail_operation_update_account (ModestMailOperation *self,
1104 const gchar *account_name)
1107 UpdateAccountInfo *info;
1108 ModestMailOperationPrivate *priv;
1109 ModestAccountMgr *mgr;
1110 TnyStoreAccount *modest_account;
1111 TnyTransportAccount *transport_account;
1113 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1114 g_return_val_if_fail (account_name, FALSE);
1116 /* Make sure that we have a connection, and request one
1118 * TODO: Is there some way to trigger this for every attempt to
1119 * use the network? */
1120 if (!modest_platform_connect_and_wait(NULL))
1123 /* Init mail operation. Set total and done to 0, and do not
1124 update them, this way the progress objects will know that
1125 we have no clue about the number of the objects */
1126 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1129 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1131 /* Get the Modest account */
1132 modest_account = (TnyStoreAccount *)
1133 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1135 TNY_ACCOUNT_TYPE_STORE);
1137 if (!modest_account) {
1138 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1139 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1140 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1141 "cannot get tny store account for %s\n", account_name);
1142 modest_mail_operation_notify_end (self);
1148 /* Get the transport account, we can not do it in the thread
1149 due to some problems with dbus */
1150 transport_account = (TnyTransportAccount *)
1151 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1153 if (!transport_account) {
1154 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1155 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1156 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1157 "cannot get tny transport account for %s\n", account_name);
1158 modest_mail_operation_notify_end (self);
1163 /* Create the helper object */
1164 info = g_slice_new (UpdateAccountInfo);
1165 info->mail_op = self;
1166 info->account = modest_account;
1167 info->transport_account = transport_account;
1169 /* Get the message size limit */
1170 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1171 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1172 if (info->max_size == 0)
1173 info->max_size = G_MAXINT;
1175 info->max_size = info->max_size * KB;
1177 /* Get per-account retrieval type */
1178 mgr = modest_runtime_get_account_mgr ();
1179 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1180 MODEST_ACCOUNT_RETRIEVE, FALSE);
1182 /* Get per-account message amount retrieval limit */
1183 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1184 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1185 if (info->retrieve_limit == 0)
1186 info->retrieve_limit = G_MAXINT;
1188 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1190 /* Set account busy */
1191 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1192 priv->account_name = g_strdup(account_name);
1194 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1199 /* ******************************************************************* */
1200 /* ************************** STORE ACTIONS ************************* */
1201 /* ******************************************************************* */
1205 modest_mail_operation_create_folder (ModestMailOperation *self,
1206 TnyFolderStore *parent,
1209 ModestMailOperationPrivate *priv;
1210 TnyFolder *new_folder = NULL;
1212 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1213 g_return_val_if_fail (name, NULL);
1215 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1218 if (TNY_IS_FOLDER (parent)) {
1219 /* Check folder rules */
1220 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1221 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1222 /* Set status failed and set an error */
1223 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1224 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1225 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1226 _("mail_in_ui_folder_create_error"));
1231 /* Create the folder */
1232 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1233 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1235 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1238 /* Notify about operation end */
1239 modest_mail_operation_notify_end (self);
1245 modest_mail_operation_remove_folder (ModestMailOperation *self,
1247 gboolean remove_to_trash)
1249 TnyAccount *account;
1250 ModestMailOperationPrivate *priv;
1251 ModestTnyFolderRules rules;
1253 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1254 g_return_if_fail (TNY_IS_FOLDER (folder));
1256 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1258 /* Check folder rules */
1259 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1260 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1261 /* Set status failed and set an error */
1262 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1263 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1264 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1265 _("mail_in_ui_folder_delete_error"));
1269 /* Get the account */
1270 account = modest_tny_folder_get_account (folder);
1271 priv->account = g_object_ref(account);
1273 /* Delete folder or move to trash */
1274 if (remove_to_trash) {
1275 TnyFolder *trash_folder = NULL;
1276 trash_folder = modest_tny_account_get_special_folder (account,
1277 TNY_FOLDER_TYPE_TRASH);
1278 /* TODO: error_handling */
1279 modest_mail_operation_xfer_folder (self, folder,
1280 TNY_FOLDER_STORE (trash_folder), TRUE);
1282 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1284 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1285 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1288 g_object_unref (G_OBJECT (parent));
1290 g_object_unref (G_OBJECT (account));
1293 /* Notify about operation end */
1294 modest_mail_operation_notify_end (self);
1298 transfer_folder_status_cb (GObject *obj,
1302 ModestMailOperation *self;
1303 ModestMailOperationPrivate *priv;
1304 ModestMailOperationState *state;
1306 g_return_if_fail (status != NULL);
1307 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1309 self = MODEST_MAIL_OPERATION (user_data);
1310 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1312 if ((status->position == 1) && (status->of_total == 100))
1315 priv->done = status->position;
1316 priv->total = status->of_total;
1318 state = modest_mail_operation_clone_state (self);
1319 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1320 g_slice_free (ModestMailOperationState, state);
1325 transfer_folder_cb (TnyFolder *folder,
1326 TnyFolderStore *into,
1328 TnyFolder *new_folder, GError **err,
1331 ModestMailOperation *self = NULL;
1332 ModestMailOperationPrivate *priv = NULL;
1334 self = MODEST_MAIL_OPERATION (user_data);
1336 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1339 priv->error = g_error_copy (*err);
1341 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1342 } else if (cancelled) {
1343 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1344 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1345 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1346 _("Transference of %s was cancelled."),
1347 tny_folder_get_name (folder));
1350 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1354 g_object_unref (folder);
1355 g_object_unref (into);
1356 if (new_folder != NULL)
1357 g_object_unref (new_folder);
1359 /* Notify about operation end */
1360 modest_mail_operation_notify_end (self);
1364 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1366 TnyFolderStore *parent,
1367 gboolean delete_original)
1369 ModestMailOperationPrivate *priv = NULL;
1370 ModestTnyFolderRules parent_rules, rules;
1372 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1373 g_return_if_fail (TNY_IS_FOLDER (folder));
1374 g_return_if_fail (TNY_IS_FOLDER (parent));
1376 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1378 /* Get account and set it into mail_operation */
1379 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1380 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1382 /* Get folder rules */
1383 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1384 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1386 if (!TNY_IS_FOLDER_STORE (parent)) {
1390 /* The moveable restriction is applied also to copy operation */
1391 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1392 /* Set status failed and set an error */
1393 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1394 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1395 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1396 _("mail_in_ui_folder_move_target_error"));
1398 /* Notify the queue */
1399 modest_mail_operation_notify_end (self);
1400 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1401 /* Set status failed and set an error */
1402 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1403 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1404 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1405 _("FIXME: parent folder does not accept new folders"));
1407 /* Notify the queue */
1408 modest_mail_operation_notify_end (self);
1410 /* Pick references for async calls */
1411 g_object_ref (folder);
1412 g_object_ref (parent);
1414 /* Move/Copy folder */
1415 tny_folder_copy_async (folder,
1417 tny_folder_get_name (folder),
1420 transfer_folder_status_cb,
1426 modest_mail_operation_rename_folder (ModestMailOperation *self,
1430 ModestMailOperationPrivate *priv;
1431 ModestTnyFolderRules rules;
1433 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1434 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1435 g_return_if_fail (name);
1437 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1439 /* Get account and set it into mail_operation */
1440 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1442 /* Check folder rules */
1443 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1444 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1445 /* Set status failed and set an error */
1446 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1447 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1448 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1449 _("FIXME: unable to rename"));
1451 /* Notify about operation end */
1452 modest_mail_operation_notify_end (self);
1454 /* Rename. Camel handles folder subscription/unsubscription */
1455 TnyFolderStore *into;
1457 into = tny_folder_get_folder_store (folder);
1458 tny_folder_copy_async (folder, into, name, TRUE,
1460 transfer_folder_status_cb,
1463 g_object_unref (into);
1468 /* ******************************************************************* */
1469 /* ************************** MSG ACTIONS ************************* */
1470 /* ******************************************************************* */
1472 void modest_mail_operation_get_msg (ModestMailOperation *self,
1474 GetMsgAsyncUserCallback user_callback,
1477 GetMsgAsyncHelper *helper = NULL;
1479 ModestMailOperationPrivate *priv;
1481 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1482 g_return_if_fail (TNY_IS_HEADER (header));
1484 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1485 folder = tny_header_get_folder (header);
1487 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1489 /* Get message from folder */
1491 /* Get account and set it into mail_operation */
1492 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1494 helper = g_slice_new0 (GetMsgAsyncHelper);
1495 helper->mail_op = self;
1496 helper->user_callback = user_callback;
1497 helper->user_data = user_data;
1498 helper->header = g_object_ref (header);
1500 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1502 g_object_unref (G_OBJECT (folder));
1504 /* Set status failed and set an error */
1505 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1506 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1507 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1508 _("Error trying to get a message. No folder found for header"));
1510 /* Notify the queue */
1511 modest_mail_operation_notify_end (self);
1516 get_msg_cb (TnyFolder *folder,
1522 GetMsgAsyncHelper *helper = NULL;
1523 ModestMailOperation *self = NULL;
1524 ModestMailOperationPrivate *priv = NULL;
1526 helper = (GetMsgAsyncHelper *) user_data;
1527 g_return_if_fail (helper != NULL);
1528 self = helper->mail_op;
1529 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1530 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1532 /* Check errors and cancel */
1534 priv->error = g_error_copy (*error);
1535 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1539 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1540 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1541 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1542 _("Error trying to refresh the contents of %s"),
1543 tny_folder_get_name (folder));
1547 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1549 /* If user defined callback function was defined, call it */
1550 if (helper->user_callback) {
1551 helper->user_callback (self, helper->header, msg, helper->user_data);
1556 g_object_unref (helper->header);
1557 g_slice_free (GetMsgAsyncHelper, helper);
1559 /* Notify about operation end */
1560 modest_mail_operation_notify_end (self);
1564 get_msg_status_cb (GObject *obj,
1568 GetMsgAsyncHelper *helper = NULL;
1569 ModestMailOperation *self;
1570 ModestMailOperationPrivate *priv;
1571 ModestMailOperationState *state;
1573 g_return_if_fail (status != NULL);
1574 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1576 helper = (GetMsgAsyncHelper *) user_data;
1577 g_return_if_fail (helper != NULL);
1579 self = helper->mail_op;
1580 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1582 if ((status->position == 1) && (status->of_total == 100))
1588 state = modest_mail_operation_clone_state (self);
1589 state->bytes_done = status->position;
1590 state->bytes_total = status->of_total;
1591 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1592 g_slice_free (ModestMailOperationState, state);
1595 /****************************************************/
1597 ModestMailOperation *mail_op;
1599 GetMsgAsyncUserCallback user_callback;
1601 GDestroyNotify notify;
1605 GetMsgAsyncUserCallback user_callback;
1609 ModestMailOperation *mail_op;
1610 } NotifyGetMsgsInfo;
1614 * Used by get_msgs_full_thread to call the user_callback for each
1615 * message that has been read
1618 notify_get_msgs_full (gpointer data)
1620 NotifyGetMsgsInfo *info;
1622 info = (NotifyGetMsgsInfo *) data;
1624 /* Call the user callback */
1625 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1627 g_slice_free (NotifyGetMsgsInfo, info);
1633 * Used by get_msgs_full_thread to free al the thread resources and to
1634 * call the destroy function for the passed user_data
1637 get_msgs_full_destroyer (gpointer data)
1639 GetFullMsgsInfo *info;
1641 info = (GetFullMsgsInfo *) data;
1644 info->notify (info->user_data);
1647 g_object_unref (info->headers);
1648 g_slice_free (GetFullMsgsInfo, info);
1654 get_msgs_full_thread (gpointer thr_user_data)
1656 GetFullMsgsInfo *info;
1657 ModestMailOperationPrivate *priv = NULL;
1658 TnyIterator *iter = NULL;
1660 info = (GetFullMsgsInfo *) thr_user_data;
1661 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1663 iter = tny_list_create_iterator (info->headers);
1664 while (!tny_iterator_is_done (iter)) {
1668 header = TNY_HEADER (tny_iterator_get_current (iter));
1669 folder = tny_header_get_folder (header);
1671 /* Get message from folder */
1674 /* The callback will call it per each header */
1675 msg = tny_folder_get_msg (folder, header, &(priv->error));
1678 ModestMailOperationState *state;
1683 /* notify progress */
1684 state = modest_mail_operation_clone_state (info->mail_op);
1685 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1686 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1687 pair, (GDestroyNotify) modest_pair_free);
1689 /* The callback is the responsible for
1690 freeing the message */
1691 if (info->user_callback) {
1692 NotifyGetMsgsInfo *info_notify;
1693 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1694 info_notify->user_callback = info->user_callback;
1695 info_notify->mail_op = info->mail_op;
1696 info_notify->header = g_object_ref (header);
1697 info_notify->msg = g_object_ref (msg);
1698 info_notify->user_data = info->user_data;
1699 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1700 notify_get_msgs_full,
1703 g_object_unref (msg);
1706 /* Set status failed and set an error */
1707 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1708 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1709 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1710 "Error trying to get a message. No folder found for header");
1712 g_object_unref (header);
1713 tny_iterator_next (iter);
1716 /* Set operation status */
1717 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1718 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1720 /* Notify about operation end */
1721 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1723 /* Free thread resources. Will be called after all previous idles */
1724 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1730 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1731 TnyList *header_list,
1732 GetMsgAsyncUserCallback user_callback,
1734 GDestroyNotify notify)
1736 TnyHeader *header = NULL;
1737 TnyFolder *folder = NULL;
1739 ModestMailOperationPrivate *priv = NULL;
1740 GetFullMsgsInfo *info = NULL;
1741 gboolean size_ok = TRUE;
1743 TnyIterator *iter = NULL;
1745 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1747 /* Init mail operation */
1748 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1749 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1751 priv->total = tny_list_get_length(header_list);
1753 /* Get account and set it into mail_operation */
1754 if (tny_list_get_length (header_list) >= 1) {
1755 iter = tny_list_create_iterator (header_list);
1756 header = TNY_HEADER (tny_iterator_get_current (iter));
1757 folder = tny_header_get_folder (header);
1758 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1759 g_object_unref (header);
1760 g_object_unref (folder);
1762 if (tny_list_get_length (header_list) == 1) {
1763 g_object_unref (iter);
1768 /* Get msg size limit */
1769 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1770 MODEST_CONF_MSG_SIZE_LIMIT,
1773 g_clear_error (&(priv->error));
1774 max_size = G_MAXINT;
1776 max_size = max_size * KB;
1779 /* Check message size limits. If there is only one message
1780 always retrieve it */
1782 while (!tny_iterator_is_done (iter) && size_ok) {
1783 header = TNY_HEADER (tny_iterator_get_current (iter));
1784 if (tny_header_get_message_size (header) >= max_size)
1786 g_object_unref (header);
1787 tny_iterator_next (iter);
1789 g_object_unref (iter);
1793 /* Create the info */
1794 info = g_slice_new0 (GetFullMsgsInfo);
1795 info->mail_op = self;
1796 info->user_callback = user_callback;
1797 info->user_data = user_data;
1798 info->headers = g_object_ref (header_list);
1799 info->notify = notify;
1801 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1803 /* Set status failed and set an error */
1804 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1805 /* FIXME: the error msg is different for pop */
1806 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1807 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1808 _("emev_ni_ui_imap_msg_size_exceed_error"));
1809 /* Remove from queue and free resources */
1810 modest_mail_operation_notify_end (self);
1818 modest_mail_operation_remove_msg (ModestMailOperation *self,
1820 gboolean remove_to_trash)
1823 ModestMailOperationPrivate *priv;
1825 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1826 g_return_if_fail (TNY_IS_HEADER (header));
1828 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1829 folder = tny_header_get_folder (header);
1831 /* Get account and set it into mail_operation */
1832 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1834 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1836 /* Delete or move to trash */
1837 if (remove_to_trash) {
1838 TnyFolder *trash_folder;
1839 TnyStoreAccount *store_account;
1841 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1842 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1843 TNY_FOLDER_TYPE_TRASH);
1848 headers = tny_simple_list_new ();
1849 tny_list_append (headers, G_OBJECT (header));
1850 g_object_unref (header);
1853 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1854 g_object_unref (headers);
1855 /* g_object_unref (trash_folder); */
1857 ModestMailOperationPrivate *priv;
1859 /* Set status failed and set an error */
1860 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1861 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1862 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1863 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1864 _("Error trying to delete a message. Trash folder not found"));
1867 g_object_unref (G_OBJECT (store_account));
1869 tny_folder_remove_msg (folder, header, &(priv->error));
1871 tny_folder_sync(folder, TRUE, &(priv->error));
1876 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1878 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1881 g_object_unref (G_OBJECT (folder));
1883 /* Notify about operation end */
1884 modest_mail_operation_notify_end (self);
1888 transfer_msgs_status_cb (GObject *obj,
1892 XFerMsgAsyncHelper *helper = NULL;
1893 ModestMailOperation *self;
1894 ModestMailOperationPrivate *priv;
1895 ModestMailOperationState *state;
1898 g_return_if_fail (status != NULL);
1899 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1901 helper = (XFerMsgAsyncHelper *) user_data;
1902 g_return_if_fail (helper != NULL);
1904 self = helper->mail_op;
1905 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1907 if ((status->position == 1) && (status->of_total == 100))
1910 priv->done = status->position;
1911 priv->total = status->of_total;
1913 state = modest_mail_operation_clone_state (self);
1914 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1915 g_slice_free (ModestMailOperationState, state);
1920 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1922 XFerMsgAsyncHelper *helper;
1923 ModestMailOperation *self;
1924 ModestMailOperationPrivate *priv;
1926 helper = (XFerMsgAsyncHelper *) user_data;
1927 self = helper->mail_op;
1929 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1932 priv->error = g_error_copy (*err);
1934 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1935 } else if (cancelled) {
1936 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1937 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1938 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1939 _("Error trying to refresh the contents of %s"),
1940 tny_folder_get_name (folder));
1943 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1946 /* If user defined callback function was defined, call it */
1947 if (helper->user_callback) {
1948 helper->user_callback (priv->source, helper->user_data);
1952 g_object_unref (helper->headers);
1953 g_object_unref (helper->dest_folder);
1954 g_object_unref (helper->mail_op);
1955 g_slice_free (XFerMsgAsyncHelper, helper);
1956 g_object_unref (folder);
1958 /* Notify about operation end */
1959 modest_mail_operation_notify_end (self);
1963 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1966 gboolean delete_original,
1967 XferMsgsAsynUserCallback user_callback,
1970 ModestMailOperationPrivate *priv;
1972 TnyFolder *src_folder;
1973 XFerMsgAsyncHelper *helper;
1975 ModestTnyFolderRules rules;
1977 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1978 g_return_if_fail (TNY_IS_LIST (headers));
1979 g_return_if_fail (TNY_IS_FOLDER (folder));
1981 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1984 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1986 /* Apply folder rules */
1987 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1989 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1990 /* Set status failed and set an error */
1991 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1992 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1993 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1994 _("FIXME: folder does not accept msgs"));
1995 /* Notify the queue */
1996 modest_mail_operation_notify_end (self);
2000 /* Create the helper */
2001 helper = g_slice_new0 (XFerMsgAsyncHelper);
2002 helper->mail_op = g_object_ref(self);
2003 helper->dest_folder = g_object_ref(folder);
2004 helper->headers = g_object_ref(headers);
2005 helper->user_callback = user_callback;
2006 helper->user_data = user_data;
2008 /* Get source folder */
2009 iter = tny_list_create_iterator (headers);
2010 header = TNY_HEADER (tny_iterator_get_current (iter));
2011 src_folder = tny_header_get_folder (header);
2012 g_object_unref (header);
2013 g_object_unref (iter);
2015 /* Get account and set it into mail_operation */
2016 priv->account = modest_tny_folder_get_account (src_folder);
2018 /* Transfer messages */
2019 tny_folder_transfer_msgs_async (src_folder,
2024 transfer_msgs_status_cb,
2030 on_refresh_folder (TnyFolder *folder,
2035 RefreshAsyncHelper *helper = NULL;
2036 ModestMailOperation *self = NULL;
2037 ModestMailOperationPrivate *priv = NULL;
2039 helper = (RefreshAsyncHelper *) user_data;
2040 self = helper->mail_op;
2041 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2044 priv->error = g_error_copy (*error);
2045 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2050 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2051 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2052 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2053 _("Error trying to refresh the contents of %s"),
2054 tny_folder_get_name (folder));
2058 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2061 /* Call user defined callback, if it exists */
2062 if (helper->user_callback)
2063 helper->user_callback (priv->source, folder, helper->user_data);
2066 g_object_unref (helper->mail_op);
2067 g_slice_free (RefreshAsyncHelper, helper);
2068 g_object_unref (folder);
2070 /* Notify about operation end */
2071 modest_mail_operation_notify_end (self);
2075 on_refresh_folder_status_update (GObject *obj,
2079 RefreshAsyncHelper *helper = NULL;
2080 ModestMailOperation *self = NULL;
2081 ModestMailOperationPrivate *priv = NULL;
2082 ModestMailOperationState *state;
2084 g_return_if_fail (user_data != NULL);
2085 g_return_if_fail (status != NULL);
2086 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2088 helper = (RefreshAsyncHelper *) user_data;
2089 self = helper->mail_op;
2090 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2092 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2094 priv->done = status->position;
2095 priv->total = status->of_total;
2097 state = modest_mail_operation_clone_state (self);
2098 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2099 g_slice_free (ModestMailOperationState, state);
2103 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2105 RefreshAsyncUserCallback user_callback,
2108 ModestMailOperationPrivate *priv = NULL;
2109 RefreshAsyncHelper *helper = NULL;
2111 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2113 /* Pick a reference */
2114 g_object_ref (folder);
2116 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2118 /* Get account and set it into mail_operation */
2119 priv->account = modest_tny_folder_get_account (folder);
2121 /* Create the helper */
2122 helper = g_slice_new0 (RefreshAsyncHelper);
2123 helper->mail_op = g_object_ref(self);
2124 helper->user_callback = user_callback;
2125 helper->user_data = user_data;
2127 /* Refresh the folder. TODO: tinymail could issue a status
2128 updates before the callback call then this could happen. We
2129 must review the design */
2130 tny_folder_refresh_async (folder,
2132 on_refresh_folder_status_update,
2138 * It's used by the mail operation queue to notify the observers
2139 * attached to that signal that the operation finished. We need to use
2140 * that because tinymail does not give us the progress of a given
2141 * operation when it finishes (it directly calls the operation
2145 modest_mail_operation_notify_end (ModestMailOperation *self)
2147 ModestMailOperationState *state;
2148 ModestMailOperationPrivate *priv = NULL;
2150 g_return_if_fail (self);
2152 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2155 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2159 /* Set the account back to not busy */
2160 if (priv->account_name) {
2161 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2163 g_free(priv->account_name);
2164 priv->account_name = NULL;
2167 /* Notify the observers about the mail opertation end */
2168 state = modest_mail_operation_clone_state (self);
2169 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2170 g_slice_free (ModestMailOperationState, state);