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;
670 UpdateAccountCallback callback;
674 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
675 /* We use this folder observer to track the headers that have been
676 * added to a folder */
679 TnyList *new_headers;
680 } InternalFolderObserver;
684 } InternalFolderObserverClass;
686 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
688 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
689 internal_folder_observer,
691 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
695 foreach_add_item (gpointer header, gpointer user_data)
697 /* printf("DEBUG: %s: header subject=%s\n",
698 * __FUNCTION__, tny_header_get_subject(TNY_HEADER(header)));
700 tny_list_prepend (TNY_LIST (user_data),
701 g_object_ref (G_OBJECT (header)));
704 /* This is the method that looks for new messages in a folder */
706 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
708 InternalFolderObserver *derived = (InternalFolderObserver *)self;
710 TnyFolderChangeChanged changed;
712 changed = tny_folder_change_get_changed (change);
714 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
717 /* Get added headers */
718 list = tny_simple_list_new ();
719 tny_folder_change_get_added_headers (change, list);
721 /* printf ("DEBUG: %s: Calling foreach with a list of size=%d\n",
722 * __FUNCTION__, tny_list_get_length(list));
725 /* Add them to the folder observer */
726 tny_list_foreach (list, foreach_add_item,
727 derived->new_headers);
729 g_object_unref (G_OBJECT (list));
734 internal_folder_observer_init (InternalFolderObserver *self)
736 self->new_headers = tny_simple_list_new ();
739 internal_folder_observer_finalize (GObject *object)
741 InternalFolderObserver *self;
743 self = (InternalFolderObserver *) object;
744 g_object_unref (self->new_headers);
746 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
749 tny_folder_observer_init (TnyFolderObserverIface *iface)
751 iface->update_func = internal_folder_observer_update;
754 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
756 GObjectClass *object_class;
758 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
759 object_class = (GObjectClass*) klass;
760 object_class->finalize = internal_folder_observer_finalize;
766 recurse_folders (TnyFolderStore *store, TnyFolderStoreQuery *query, TnyList *all_folders)
769 TnyList *folders = tny_simple_list_new ();
771 tny_folder_store_get_folders (store, folders, query, NULL);
772 iter = tny_list_create_iterator (folders);
774 while (!tny_iterator_is_done (iter)) {
776 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
778 tny_list_prepend (all_folders, G_OBJECT (folder));
779 recurse_folders (folder, query, all_folders);
780 g_object_unref (G_OBJECT (folder));
782 tny_iterator_next (iter);
784 g_object_unref (G_OBJECT (iter));
785 g_object_unref (G_OBJECT (folders));
789 * Issues the "progress-changed" signal. The timer won't be removed,
790 * so you must call g_source_remove to stop the signal emission
793 idle_notify_progress (gpointer data)
795 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
796 ModestMailOperationState *state;
798 state = modest_mail_operation_clone_state (mail_op);
799 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
800 g_slice_free (ModestMailOperationState, state);
806 * Issues the "progress-changed" signal and removes the timer. It uses
807 * a lock to ensure that the progress information of the mail
808 * operation is not modified while there are notifications pending
811 idle_notify_progress_once (gpointer data)
815 pair = (ModestPair *) data;
817 g_signal_emit (G_OBJECT (pair->first), signals[PROGRESS_CHANGED_SIGNAL], 0, pair->second, NULL);
819 /* Free the state and the reference to the mail operation */
820 g_slice_free (ModestMailOperationState, (ModestMailOperationState*)pair->second);
821 g_object_unref (pair->first);
827 * Used by update_account_thread to notify the queue from the main
828 * loop. We call it inside an idle call to achieve that
831 notify_update_account_queue (gpointer data)
833 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
834 ModestMailOperationPrivate *priv = NULL;
836 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(mail_op);
838 modest_mail_operation_notify_end (mail_op);
839 g_object_unref (mail_op);
845 compare_headers_by_date (gconstpointer a,
848 TnyHeader **header1, **header2;
851 header1 = (TnyHeader **) a;
852 header2 = (TnyHeader **) b;
854 sent1 = tny_header_get_date_sent (*header1);
855 sent2 = tny_header_get_date_sent (*header2);
857 /* We want the most recent ones (greater time_t) at the
866 set_last_updated_idle (gpointer data)
868 /* It does not matter if the time is not exactly the same than
869 the time when this idle was called, it's just an
870 approximation and it won't be very different */
871 modest_account_mgr_set_int (modest_runtime_get_account_mgr (),
873 MODEST_ACCOUNT_LAST_UPDATED,
881 update_account_thread (gpointer thr_user_data)
883 static gboolean first_time = TRUE;
884 UpdateAccountInfo *info;
885 TnyList *all_folders = NULL;
886 GPtrArray *new_headers = NULL;
887 TnyIterator *iter = NULL;
888 TnyFolderStoreQuery *query = NULL;
889 ModestMailOperationPrivate *priv = NULL;
890 ModestTnySendQueue *send_queue = NULL;
892 info = (UpdateAccountInfo *) thr_user_data;
893 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
895 /* Get account and set it into mail_operation */
896 priv->account = g_object_ref (info->account);
899 * for POP3, we do a logout-login upon send/receive -- many POP-servers (like Gmail) do not
900 * show any updates unless we do that
902 if (!first_time && TNY_IS_CAMEL_POP_STORE_ACCOUNT(priv->account))
903 tny_camel_pop_store_account_reconnect (TNY_CAMEL_POP_STORE_ACCOUNT(priv->account));
905 /* Get all the folders. We can do it synchronously because
906 we're already running in a different thread than the UI */
907 all_folders = tny_simple_list_new ();
908 query = tny_folder_store_query_new ();
909 tny_folder_store_query_add_item (query, NULL, TNY_FOLDER_STORE_QUERY_OPTION_SUBSCRIBED);
910 tny_folder_store_get_folders (TNY_FOLDER_STORE (info->account),
915 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
919 iter = tny_list_create_iterator (all_folders);
920 while (!tny_iterator_is_done (iter)) {
921 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
923 recurse_folders (folder, query, all_folders);
924 tny_iterator_next (iter);
926 g_object_unref (G_OBJECT (iter));
928 /* Update status and notify. We need to call the notification
929 with a source function in order to call it from the main
930 loop. We need that in order not to get into trouble with
931 Gtk+. We use a timeout in order to provide more status
932 information, because the sync tinymail call does not
933 provide it for the moment */
934 gint timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
936 /* Refresh folders */
937 new_headers = g_ptr_array_new ();
938 iter = tny_list_create_iterator (all_folders);
940 while (!tny_iterator_is_done (iter) && !priv->error && !did_a_cancel) {
942 InternalFolderObserver *observer;
943 TnyFolderStore *folder = TNY_FOLDER_STORE (tny_iterator_get_current (iter));
945 /* Refresh the folder */
946 /* Our observer receives notification of new emails during folder refreshes,
947 * so we can use observer->new_headers.
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);
987 g_object_unref (G_OBJECT (folder));
990 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
994 tny_iterator_next (iter);
997 did_a_cancel = FALSE;
999 g_object_unref (G_OBJECT (iter));
1000 g_source_remove (timeout);
1002 if (new_headers->len > 0) {
1006 g_ptr_array_sort (new_headers, (GCompareFunc) compare_headers_by_date);
1008 /* Apply message count limit */
1009 /* If the number of messages exceeds the maximum, ask the
1010 * user to download them all,
1011 * as per the UI spec "Retrieval Limits" section in 4.4:
1013 if (new_headers->len > info->retrieve_limit) {
1014 /* TODO: Ask the user, instead of just
1016 * mail_nc_msg_count_limit_exceeded, with 'Get
1017 * all' and 'Newest only' buttons. */
1018 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1019 MODEST_MAIL_OPERATION_ERROR_RETRIEVAL_NUMBER_LIMIT,
1020 "The number of messages to retrieve exceeds the chosen limit for account %s\n",
1021 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1022 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1027 priv->total = MIN (new_headers->len, info->retrieve_limit);
1028 while (msg_num < priv->total) {
1030 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers, msg_num));
1031 TnyFolder *folder = tny_header_get_folder (header);
1032 TnyMsg *msg = tny_folder_get_msg (folder, header, NULL);
1033 ModestMailOperationState *state;
1037 /* We can not just use the mail operation because the
1038 values of done and total could change before the
1040 state = modest_mail_operation_clone_state (info->mail_op);
1041 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1042 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1043 pair, (GDestroyNotify) modest_pair_free);
1045 g_object_unref (msg);
1046 g_object_unref (folder);
1050 g_ptr_array_foreach (new_headers, (GFunc) g_object_unref, NULL);
1051 g_ptr_array_free (new_headers, FALSE);
1055 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
1058 if (priv->account != NULL)
1059 g_object_unref (priv->account);
1060 priv->account = g_object_ref (info->transport_account);
1062 send_queue = modest_runtime_get_send_queue (info->transport_account);
1064 timeout = g_timeout_add (250, idle_notify_progress, info->mail_op);
1065 modest_tny_send_queue_try_to_send (send_queue);
1066 g_source_remove (timeout);
1068 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1069 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1070 "cannot create a send queue for %s\n",
1071 tny_account_get_name (TNY_ACCOUNT (info->transport_account)));
1072 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1075 /* Check if the operation was a success */
1077 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1079 /* Update the last updated key */
1080 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1081 set_last_updated_idle,
1082 g_strdup (tny_account_get_id (TNY_ACCOUNT (info->account))),
1083 (GDestroyNotify) g_free);
1087 /* Notify about operation end. Note that the info could be
1088 freed before this idle happens, but the mail operation will
1090 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1093 info->callback (info->mail_op,
1094 (new_headers) ? new_headers->len : 0,
1098 g_object_unref (query);
1099 g_object_unref (all_folders);
1100 g_object_unref (info->account);
1101 g_object_unref (info->transport_account);
1102 g_free (info->retrieve_type);
1103 g_slice_free (UpdateAccountInfo, info);
1111 modest_mail_operation_update_account (ModestMailOperation *self,
1112 const gchar *account_name,
1113 UpdateAccountCallback callback,
1117 UpdateAccountInfo *info;
1118 ModestMailOperationPrivate *priv;
1119 ModestAccountMgr *mgr;
1120 TnyStoreAccount *modest_account;
1121 TnyTransportAccount *transport_account;
1123 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1124 g_return_val_if_fail (account_name, FALSE);
1126 /* Init mail operation. Set total and done to 0, and do not
1127 update them, this way the progress objects will know that
1128 we have no clue about the number of the objects */
1129 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1132 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1134 /* Make sure that we have a connection, and request one
1136 * TODO: Is there some way to trigger this for every attempt to
1137 * use the network? */
1138 if (!modest_platform_connect_and_wait(NULL))
1141 /* Get the Modest account */
1142 modest_account = (TnyStoreAccount *)
1143 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1145 TNY_ACCOUNT_TYPE_STORE);
1147 if (!modest_account) {
1148 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1149 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1150 "cannot get tny store account for %s\n", account_name);
1155 /* Get the transport account, we can not do it in the thread
1156 due to some problems with dbus */
1157 transport_account = (TnyTransportAccount *)
1158 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1160 if (!transport_account) {
1161 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1162 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1163 "cannot get tny transport account for %s\n", account_name);
1167 /* Create the helper object */
1168 info = g_slice_new (UpdateAccountInfo);
1169 info->mail_op = self;
1170 info->account = modest_account;
1171 info->transport_account = transport_account;
1172 info->callback = callback;
1173 info->user_data = user_data;
1175 /* Get the message size limit */
1176 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1177 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1178 if (info->max_size == 0)
1179 info->max_size = G_MAXINT;
1181 info->max_size = info->max_size * KB;
1183 /* Get per-account retrieval type */
1184 mgr = modest_runtime_get_account_mgr ();
1185 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1186 MODEST_ACCOUNT_RETRIEVE, FALSE);
1188 /* Get per-account message amount retrieval limit */
1189 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1190 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1191 if (info->retrieve_limit == 0)
1192 info->retrieve_limit = G_MAXINT;
1194 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1196 /* Set account busy */
1197 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1198 priv->account_name = g_strdup(account_name);
1200 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1205 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1207 callback (self, 0, user_data);
1208 modest_mail_operation_notify_end (self);
1212 /* ******************************************************************* */
1213 /* ************************** STORE ACTIONS ************************* */
1214 /* ******************************************************************* */
1218 modest_mail_operation_create_folder (ModestMailOperation *self,
1219 TnyFolderStore *parent,
1222 ModestMailOperationPrivate *priv;
1223 TnyFolder *new_folder = NULL;
1225 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1226 g_return_val_if_fail (name, NULL);
1228 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1231 if (TNY_IS_FOLDER (parent)) {
1232 /* Check folder rules */
1233 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1234 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1235 /* Set status failed and set an error */
1236 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1237 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1238 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1239 _("mail_in_ui_folder_create_error"));
1244 /* Create the folder */
1245 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1246 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1248 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1251 /* Notify about operation end */
1252 modest_mail_operation_notify_end (self);
1258 modest_mail_operation_remove_folder (ModestMailOperation *self,
1260 gboolean remove_to_trash)
1262 TnyAccount *account;
1263 ModestMailOperationPrivate *priv;
1264 ModestTnyFolderRules rules;
1266 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1267 g_return_if_fail (TNY_IS_FOLDER (folder));
1269 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1271 /* Check folder rules */
1272 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1273 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1274 /* Set status failed and set an error */
1275 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1276 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1277 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1278 _("mail_in_ui_folder_delete_error"));
1282 /* Get the account */
1283 account = modest_tny_folder_get_account (folder);
1284 priv->account = g_object_ref(account);
1286 /* Delete folder or move to trash */
1287 if (remove_to_trash) {
1288 TnyFolder *trash_folder = NULL;
1289 trash_folder = modest_tny_account_get_special_folder (account,
1290 TNY_FOLDER_TYPE_TRASH);
1291 /* TODO: error_handling */
1292 modest_mail_operation_xfer_folder (self, folder,
1293 TNY_FOLDER_STORE (trash_folder), TRUE);
1295 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1297 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1298 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1301 g_object_unref (G_OBJECT (parent));
1303 g_object_unref (G_OBJECT (account));
1306 /* Notify about operation end */
1307 modest_mail_operation_notify_end (self);
1311 transfer_folder_status_cb (GObject *obj,
1315 ModestMailOperation *self;
1316 ModestMailOperationPrivate *priv;
1317 ModestMailOperationState *state;
1319 g_return_if_fail (status != NULL);
1320 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1322 self = MODEST_MAIL_OPERATION (user_data);
1323 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1325 if ((status->position == 1) && (status->of_total == 100))
1328 priv->done = status->position;
1329 priv->total = status->of_total;
1331 state = modest_mail_operation_clone_state (self);
1332 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1333 g_slice_free (ModestMailOperationState, state);
1338 transfer_folder_cb (TnyFolder *folder,
1339 TnyFolderStore *into,
1341 TnyFolder *new_folder, GError **err,
1344 ModestMailOperation *self = NULL;
1345 ModestMailOperationPrivate *priv = NULL;
1347 self = MODEST_MAIL_OPERATION (user_data);
1349 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1352 priv->error = g_error_copy (*err);
1354 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1355 } else if (cancelled) {
1356 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1357 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1358 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1359 _("Transference of %s was cancelled."),
1360 tny_folder_get_name (folder));
1363 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1367 g_object_unref (folder);
1368 g_object_unref (into);
1369 if (new_folder != NULL)
1370 g_object_unref (new_folder);
1372 /* Notify about operation end */
1373 modest_mail_operation_notify_end (self);
1377 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1379 TnyFolderStore *parent,
1380 gboolean delete_original)
1382 ModestMailOperationPrivate *priv = NULL;
1383 ModestTnyFolderRules parent_rules, rules;
1385 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1386 g_return_if_fail (TNY_IS_FOLDER (folder));
1387 g_return_if_fail (TNY_IS_FOLDER (parent));
1389 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1391 /* Get account and set it into mail_operation */
1392 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1393 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1395 /* Get folder rules */
1396 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1397 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1399 if (!TNY_IS_FOLDER_STORE (parent)) {
1403 /* The moveable restriction is applied also to copy operation */
1404 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1405 /* Set status failed and set an error */
1406 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1407 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1408 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1409 _("mail_in_ui_folder_move_target_error"));
1411 /* Notify the queue */
1412 modest_mail_operation_notify_end (self);
1413 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1414 /* Set status failed and set an error */
1415 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1416 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1417 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1418 _("FIXME: parent folder does not accept new folders"));
1420 /* Notify the queue */
1421 modest_mail_operation_notify_end (self);
1423 /* Pick references for async calls */
1424 g_object_ref (folder);
1425 g_object_ref (parent);
1427 /* Move/Copy folder */
1428 tny_folder_copy_async (folder,
1430 tny_folder_get_name (folder),
1433 transfer_folder_status_cb,
1439 modest_mail_operation_rename_folder (ModestMailOperation *self,
1443 ModestMailOperationPrivate *priv;
1444 ModestTnyFolderRules rules;
1446 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1447 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1448 g_return_if_fail (name);
1450 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1452 /* Get account and set it into mail_operation */
1453 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1455 /* Check folder rules */
1456 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1457 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1458 /* Set status failed and set an error */
1459 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1460 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1461 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1462 _("FIXME: unable to rename"));
1464 /* Notify about operation end */
1465 modest_mail_operation_notify_end (self);
1467 /* Rename. Camel handles folder subscription/unsubscription */
1468 TnyFolderStore *into;
1470 into = tny_folder_get_folder_store (folder);
1471 tny_folder_copy_async (folder, into, name, TRUE,
1473 transfer_folder_status_cb,
1476 g_object_unref (into);
1481 /* ******************************************************************* */
1482 /* ************************** MSG ACTIONS ************************* */
1483 /* ******************************************************************* */
1485 void modest_mail_operation_get_msg (ModestMailOperation *self,
1487 GetMsgAsyncUserCallback user_callback,
1490 GetMsgAsyncHelper *helper = NULL;
1492 ModestMailOperationPrivate *priv;
1494 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1495 g_return_if_fail (TNY_IS_HEADER (header));
1497 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1498 folder = tny_header_get_folder (header);
1500 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1502 /* Get message from folder */
1504 /* Get account and set it into mail_operation */
1505 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1507 helper = g_slice_new0 (GetMsgAsyncHelper);
1508 helper->mail_op = self;
1509 helper->user_callback = user_callback;
1510 helper->user_data = user_data;
1511 helper->header = g_object_ref (header);
1513 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1515 g_object_unref (G_OBJECT (folder));
1517 /* Set status failed and set an error */
1518 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1519 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1520 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1521 _("Error trying to get a message. No folder found for header"));
1523 /* Notify the queue */
1524 modest_mail_operation_notify_end (self);
1529 get_msg_cb (TnyFolder *folder,
1535 GetMsgAsyncHelper *helper = NULL;
1536 ModestMailOperation *self = NULL;
1537 ModestMailOperationPrivate *priv = NULL;
1539 helper = (GetMsgAsyncHelper *) user_data;
1540 g_return_if_fail (helper != NULL);
1541 self = helper->mail_op;
1542 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1543 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1545 /* Check errors and cancel */
1547 priv->error = g_error_copy (*error);
1548 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1552 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1553 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1554 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1555 _("Error trying to refresh the contents of %s"),
1556 tny_folder_get_name (folder));
1560 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1562 /* If user defined callback function was defined, call it */
1563 if (helper->user_callback) {
1564 helper->user_callback (self, helper->header, msg, helper->user_data);
1569 g_object_unref (helper->header);
1570 g_slice_free (GetMsgAsyncHelper, helper);
1572 /* Notify about operation end */
1573 modest_mail_operation_notify_end (self);
1577 get_msg_status_cb (GObject *obj,
1581 GetMsgAsyncHelper *helper = NULL;
1582 ModestMailOperation *self;
1583 ModestMailOperationPrivate *priv;
1584 ModestMailOperationState *state;
1586 g_return_if_fail (status != NULL);
1587 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1589 helper = (GetMsgAsyncHelper *) user_data;
1590 g_return_if_fail (helper != NULL);
1592 self = helper->mail_op;
1593 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1595 if ((status->position == 1) && (status->of_total == 100))
1601 state = modest_mail_operation_clone_state (self);
1602 state->bytes_done = status->position;
1603 state->bytes_total = status->of_total;
1604 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1605 g_slice_free (ModestMailOperationState, state);
1608 /****************************************************/
1610 ModestMailOperation *mail_op;
1612 GetMsgAsyncUserCallback user_callback;
1614 GDestroyNotify notify;
1618 GetMsgAsyncUserCallback user_callback;
1622 ModestMailOperation *mail_op;
1623 } NotifyGetMsgsInfo;
1627 * Used by get_msgs_full_thread to call the user_callback for each
1628 * message that has been read
1631 notify_get_msgs_full (gpointer data)
1633 NotifyGetMsgsInfo *info;
1635 info = (NotifyGetMsgsInfo *) data;
1637 /* Call the user callback */
1638 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1640 g_slice_free (NotifyGetMsgsInfo, info);
1646 * Used by get_msgs_full_thread to free al the thread resources and to
1647 * call the destroy function for the passed user_data
1650 get_msgs_full_destroyer (gpointer data)
1652 GetFullMsgsInfo *info;
1654 info = (GetFullMsgsInfo *) data;
1657 info->notify (info->user_data);
1660 g_object_unref (info->headers);
1661 g_slice_free (GetFullMsgsInfo, info);
1667 get_msgs_full_thread (gpointer thr_user_data)
1669 GetFullMsgsInfo *info;
1670 ModestMailOperationPrivate *priv = NULL;
1671 TnyIterator *iter = NULL;
1673 info = (GetFullMsgsInfo *) thr_user_data;
1674 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1676 iter = tny_list_create_iterator (info->headers);
1677 while (!tny_iterator_is_done (iter)) {
1681 header = TNY_HEADER (tny_iterator_get_current (iter));
1682 folder = tny_header_get_folder (header);
1684 /* Get message from folder */
1687 /* The callback will call it per each header */
1688 msg = tny_folder_get_msg (folder, header, &(priv->error));
1691 ModestMailOperationState *state;
1696 /* notify progress */
1697 state = modest_mail_operation_clone_state (info->mail_op);
1698 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1699 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1700 pair, (GDestroyNotify) modest_pair_free);
1702 /* The callback is the responsible for
1703 freeing the message */
1704 if (info->user_callback) {
1705 NotifyGetMsgsInfo *info_notify;
1706 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1707 info_notify->user_callback = info->user_callback;
1708 info_notify->mail_op = info->mail_op;
1709 info_notify->header = g_object_ref (header);
1710 info_notify->msg = g_object_ref (msg);
1711 info_notify->user_data = info->user_data;
1712 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1713 notify_get_msgs_full,
1716 g_object_unref (msg);
1719 /* Set status failed and set an error */
1720 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1721 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1722 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1723 "Error trying to get a message. No folder found for header");
1725 g_object_unref (header);
1726 tny_iterator_next (iter);
1729 /* Set operation status */
1730 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1731 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1733 /* Notify about operation end */
1734 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1736 /* Free thread resources. Will be called after all previous idles */
1737 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1743 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1744 TnyList *header_list,
1745 GetMsgAsyncUserCallback user_callback,
1747 GDestroyNotify notify)
1749 TnyHeader *header = NULL;
1750 TnyFolder *folder = NULL;
1752 ModestMailOperationPrivate *priv = NULL;
1753 GetFullMsgsInfo *info = NULL;
1754 gboolean size_ok = TRUE;
1756 TnyIterator *iter = NULL;
1758 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1760 /* Init mail operation */
1761 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1762 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1764 priv->total = tny_list_get_length(header_list);
1766 /* Get account and set it into mail_operation */
1767 if (tny_list_get_length (header_list) >= 1) {
1768 iter = tny_list_create_iterator (header_list);
1769 header = TNY_HEADER (tny_iterator_get_current (iter));
1770 folder = tny_header_get_folder (header);
1771 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1772 g_object_unref (header);
1773 g_object_unref (folder);
1775 if (tny_list_get_length (header_list) == 1) {
1776 g_object_unref (iter);
1781 /* Get msg size limit */
1782 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1783 MODEST_CONF_MSG_SIZE_LIMIT,
1786 g_clear_error (&(priv->error));
1787 max_size = G_MAXINT;
1789 max_size = max_size * KB;
1792 /* Check message size limits. If there is only one message
1793 always retrieve it */
1795 while (!tny_iterator_is_done (iter) && size_ok) {
1796 header = TNY_HEADER (tny_iterator_get_current (iter));
1797 if (tny_header_get_message_size (header) >= max_size)
1799 g_object_unref (header);
1800 tny_iterator_next (iter);
1802 g_object_unref (iter);
1806 /* Create the info */
1807 info = g_slice_new0 (GetFullMsgsInfo);
1808 info->mail_op = self;
1809 info->user_callback = user_callback;
1810 info->user_data = user_data;
1811 info->headers = g_object_ref (header_list);
1812 info->notify = notify;
1814 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1816 /* Set status failed and set an error */
1817 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1818 /* FIXME: the error msg is different for pop */
1819 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1820 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1821 _("emev_ni_ui_imap_msg_size_exceed_error"));
1822 /* Remove from queue and free resources */
1823 modest_mail_operation_notify_end (self);
1831 modest_mail_operation_remove_msg (ModestMailOperation *self,
1833 gboolean remove_to_trash)
1836 ModestMailOperationPrivate *priv;
1838 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1839 g_return_if_fail (TNY_IS_HEADER (header));
1841 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1842 folder = tny_header_get_folder (header);
1844 /* Get account and set it into mail_operation */
1845 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1847 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1849 /* Delete or move to trash */
1850 if (remove_to_trash) {
1851 TnyFolder *trash_folder;
1852 TnyStoreAccount *store_account;
1854 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1855 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1856 TNY_FOLDER_TYPE_TRASH);
1861 headers = tny_simple_list_new ();
1862 tny_list_append (headers, G_OBJECT (header));
1863 g_object_unref (header);
1866 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1867 g_object_unref (headers);
1868 /* g_object_unref (trash_folder); */
1870 ModestMailOperationPrivate *priv;
1872 /* Set status failed and set an error */
1873 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1874 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1875 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1876 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1877 _("Error trying to delete a message. Trash folder not found"));
1880 g_object_unref (G_OBJECT (store_account));
1882 tny_folder_remove_msg (folder, header, &(priv->error));
1884 tny_folder_sync(folder, TRUE, &(priv->error));
1889 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1891 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1894 g_object_unref (G_OBJECT (folder));
1896 /* Notify about operation end */
1897 modest_mail_operation_notify_end (self);
1901 transfer_msgs_status_cb (GObject *obj,
1905 XFerMsgAsyncHelper *helper = NULL;
1906 ModestMailOperation *self;
1907 ModestMailOperationPrivate *priv;
1908 ModestMailOperationState *state;
1911 g_return_if_fail (status != NULL);
1912 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1914 helper = (XFerMsgAsyncHelper *) user_data;
1915 g_return_if_fail (helper != NULL);
1917 self = helper->mail_op;
1918 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1920 if ((status->position == 1) && (status->of_total == 100))
1923 priv->done = status->position;
1924 priv->total = status->of_total;
1926 state = modest_mail_operation_clone_state (self);
1927 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1928 g_slice_free (ModestMailOperationState, state);
1933 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1935 XFerMsgAsyncHelper *helper;
1936 ModestMailOperation *self;
1937 ModestMailOperationPrivate *priv;
1939 helper = (XFerMsgAsyncHelper *) user_data;
1940 self = helper->mail_op;
1942 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1945 priv->error = g_error_copy (*err);
1947 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1948 } else if (cancelled) {
1949 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1950 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1951 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1952 _("Error trying to refresh the contents of %s"),
1953 tny_folder_get_name (folder));
1956 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1959 /* If user defined callback function was defined, call it */
1960 if (helper->user_callback) {
1961 helper->user_callback (priv->source, helper->user_data);
1965 g_object_unref (helper->headers);
1966 g_object_unref (helper->dest_folder);
1967 g_object_unref (helper->mail_op);
1968 g_slice_free (XFerMsgAsyncHelper, helper);
1969 g_object_unref (folder);
1971 /* Notify about operation end */
1972 modest_mail_operation_notify_end (self);
1976 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1979 gboolean delete_original,
1980 XferMsgsAsynUserCallback user_callback,
1983 ModestMailOperationPrivate *priv;
1985 TnyFolder *src_folder;
1986 XFerMsgAsyncHelper *helper;
1988 ModestTnyFolderRules rules;
1990 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1991 g_return_if_fail (TNY_IS_LIST (headers));
1992 g_return_if_fail (TNY_IS_FOLDER (folder));
1994 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1997 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1999 /* Apply folder rules */
2000 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2002 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2003 /* Set status failed and set an error */
2004 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2005 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2006 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2007 _("FIXME: folder does not accept msgs"));
2008 /* Notify the queue */
2009 modest_mail_operation_notify_end (self);
2013 /* Create the helper */
2014 helper = g_slice_new0 (XFerMsgAsyncHelper);
2015 helper->mail_op = g_object_ref(self);
2016 helper->dest_folder = g_object_ref(folder);
2017 helper->headers = g_object_ref(headers);
2018 helper->user_callback = user_callback;
2019 helper->user_data = user_data;
2021 /* Get source folder */
2022 iter = tny_list_create_iterator (headers);
2023 header = TNY_HEADER (tny_iterator_get_current (iter));
2024 src_folder = tny_header_get_folder (header);
2025 g_object_unref (header);
2026 g_object_unref (iter);
2028 /* Get account and set it into mail_operation */
2029 priv->account = modest_tny_folder_get_account (src_folder);
2031 /* Transfer messages */
2032 tny_folder_transfer_msgs_async (src_folder,
2037 transfer_msgs_status_cb,
2043 on_refresh_folder (TnyFolder *folder,
2048 RefreshAsyncHelper *helper = NULL;
2049 ModestMailOperation *self = NULL;
2050 ModestMailOperationPrivate *priv = NULL;
2052 helper = (RefreshAsyncHelper *) user_data;
2053 self = helper->mail_op;
2054 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2057 priv->error = g_error_copy (*error);
2058 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2063 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2064 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2065 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2066 _("Error trying to refresh the contents of %s"),
2067 tny_folder_get_name (folder));
2071 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2074 /* Call user defined callback, if it exists */
2075 if (helper->user_callback)
2076 helper->user_callback (priv->source, folder, helper->user_data);
2079 g_object_unref (helper->mail_op);
2080 g_slice_free (RefreshAsyncHelper, helper);
2081 g_object_unref (folder);
2083 /* Notify about operation end */
2084 modest_mail_operation_notify_end (self);
2088 on_refresh_folder_status_update (GObject *obj,
2092 RefreshAsyncHelper *helper = NULL;
2093 ModestMailOperation *self = NULL;
2094 ModestMailOperationPrivate *priv = NULL;
2095 ModestMailOperationState *state;
2097 g_return_if_fail (user_data != NULL);
2098 g_return_if_fail (status != NULL);
2099 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2101 helper = (RefreshAsyncHelper *) user_data;
2102 self = helper->mail_op;
2103 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2105 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2107 priv->done = status->position;
2108 priv->total = status->of_total;
2110 state = modest_mail_operation_clone_state (self);
2111 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2112 g_slice_free (ModestMailOperationState, state);
2116 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2118 RefreshAsyncUserCallback user_callback,
2121 ModestMailOperationPrivate *priv = NULL;
2122 RefreshAsyncHelper *helper = NULL;
2124 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2126 /* Pick a reference */
2127 g_object_ref (folder);
2129 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2131 /* Get account and set it into mail_operation */
2132 priv->account = modest_tny_folder_get_account (folder);
2134 /* Create the helper */
2135 helper = g_slice_new0 (RefreshAsyncHelper);
2136 helper->mail_op = g_object_ref(self);
2137 helper->user_callback = user_callback;
2138 helper->user_data = user_data;
2140 /* Refresh the folder. TODO: tinymail could issue a status
2141 updates before the callback call then this could happen. We
2142 must review the design */
2143 tny_folder_refresh_async (folder,
2145 on_refresh_folder_status_update,
2151 * It's used by the mail operation queue to notify the observers
2152 * attached to that signal that the operation finished. We need to use
2153 * that because tinymail does not give us the progress of a given
2154 * operation when it finishes (it directly calls the operation
2158 modest_mail_operation_notify_end (ModestMailOperation *self)
2160 ModestMailOperationState *state;
2161 ModestMailOperationPrivate *priv = NULL;
2163 g_return_if_fail (self);
2165 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2168 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2172 /* Set the account back to not busy */
2173 if (priv->account_name) {
2174 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2176 g_free(priv->account_name);
2177 priv->account_name = NULL;
2180 /* Notify the observers about the mail opertation end */
2181 state = modest_mail_operation_clone_state (self);
2182 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2183 g_slice_free (ModestMailOperationState, state);