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);
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 if (new_headers->len > info->retrieve_limit) {
1011 /* TODO: Ask the user, instead of just
1013 * mail_nc_msg_count_limit_exceeded, with 'Get
1014 * 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 info->callback (info->mail_op,
1091 (new_headers) ? new_headers->len : 0,
1095 g_object_unref (query);
1096 g_object_unref (all_folders);
1097 g_object_unref (info->account);
1098 g_object_unref (info->transport_account);
1099 g_free (info->retrieve_type);
1100 g_slice_free (UpdateAccountInfo, info);
1108 modest_mail_operation_update_account (ModestMailOperation *self,
1109 const gchar *account_name,
1110 UpdateAccountCallback callback,
1114 UpdateAccountInfo *info;
1115 ModestMailOperationPrivate *priv;
1116 ModestAccountMgr *mgr;
1117 TnyStoreAccount *modest_account;
1118 TnyTransportAccount *transport_account;
1120 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
1121 g_return_val_if_fail (account_name, FALSE);
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 /* Make sure that we have a connection, and request one
1133 * TODO: Is there some way to trigger this for every attempt to
1134 * use the network? */
1135 if (!modest_platform_connect_and_wait(NULL))
1138 /* Get the Modest account */
1139 modest_account = (TnyStoreAccount *)
1140 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1142 TNY_ACCOUNT_TYPE_STORE);
1144 if (!modest_account) {
1145 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1146 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1147 "cannot get tny store account for %s\n", account_name);
1152 /* Get the transport account, we can not do it in the thread
1153 due to some problems with dbus */
1154 transport_account = (TnyTransportAccount *)
1155 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1157 if (!transport_account) {
1158 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1159 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1160 "cannot get tny transport account for %s\n", account_name);
1164 /* Create the helper object */
1165 info = g_slice_new (UpdateAccountInfo);
1166 info->mail_op = self;
1167 info->account = modest_account;
1168 info->transport_account = transport_account;
1169 info->callback = callback;
1170 info->user_data = user_data;
1172 /* Get the message size limit */
1173 info->max_size = modest_conf_get_int (modest_runtime_get_conf (),
1174 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1175 if (info->max_size == 0)
1176 info->max_size = G_MAXINT;
1178 info->max_size = info->max_size * KB;
1180 /* Get per-account retrieval type */
1181 mgr = modest_runtime_get_account_mgr ();
1182 info->retrieve_type = modest_account_mgr_get_string (mgr, account_name,
1183 MODEST_ACCOUNT_RETRIEVE, FALSE);
1185 /* Get per-account message amount retrieval limit */
1186 info->retrieve_limit = modest_account_mgr_get_int (mgr, account_name,
1187 MODEST_ACCOUNT_LIMIT_RETRIEVE, FALSE);
1188 if (info->retrieve_limit == 0)
1189 info->retrieve_limit = G_MAXINT;
1191 /* printf ("DEBUG: %s: info->retrieve_limit = %d\n", __FUNCTION__, info->retrieve_limit); */
1193 /* Set account busy */
1194 modest_account_mgr_set_account_busy(mgr, account_name, TRUE);
1195 priv->account_name = g_strdup(account_name);
1197 thread = g_thread_create (update_account_thread, info, FALSE, NULL);
1202 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1204 callback (self, 0, user_data);
1205 modest_mail_operation_notify_end (self);
1209 /* ******************************************************************* */
1210 /* ************************** STORE ACTIONS ************************* */
1211 /* ******************************************************************* */
1215 modest_mail_operation_create_folder (ModestMailOperation *self,
1216 TnyFolderStore *parent,
1219 ModestMailOperationPrivate *priv;
1220 TnyFolder *new_folder = NULL;
1222 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1223 g_return_val_if_fail (name, NULL);
1225 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1228 if (TNY_IS_FOLDER (parent)) {
1229 /* Check folder rules */
1230 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1231 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1232 /* Set status failed and set an error */
1233 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1234 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1235 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1236 _("mail_in_ui_folder_create_error"));
1241 /* Create the folder */
1242 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1243 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1245 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1248 /* Notify about operation end */
1249 modest_mail_operation_notify_end (self);
1255 modest_mail_operation_remove_folder (ModestMailOperation *self,
1257 gboolean remove_to_trash)
1259 TnyAccount *account;
1260 ModestMailOperationPrivate *priv;
1261 ModestTnyFolderRules rules;
1263 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1264 g_return_if_fail (TNY_IS_FOLDER (folder));
1266 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1268 /* Check folder rules */
1269 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1270 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1271 /* Set status failed and set an error */
1272 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1273 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1274 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1275 _("mail_in_ui_folder_delete_error"));
1279 /* Get the account */
1280 account = modest_tny_folder_get_account (folder);
1281 priv->account = g_object_ref(account);
1283 /* Delete folder or move to trash */
1284 if (remove_to_trash) {
1285 TnyFolder *trash_folder = NULL;
1286 trash_folder = modest_tny_account_get_special_folder (account,
1287 TNY_FOLDER_TYPE_TRASH);
1288 /* TODO: error_handling */
1289 modest_mail_operation_xfer_folder (self, folder,
1290 TNY_FOLDER_STORE (trash_folder), TRUE);
1292 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1294 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1295 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1298 g_object_unref (G_OBJECT (parent));
1300 g_object_unref (G_OBJECT (account));
1303 /* Notify about operation end */
1304 modest_mail_operation_notify_end (self);
1308 transfer_folder_status_cb (GObject *obj,
1312 ModestMailOperation *self;
1313 ModestMailOperationPrivate *priv;
1314 ModestMailOperationState *state;
1316 g_return_if_fail (status != NULL);
1317 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1319 self = MODEST_MAIL_OPERATION (user_data);
1320 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1322 if ((status->position == 1) && (status->of_total == 100))
1325 priv->done = status->position;
1326 priv->total = status->of_total;
1328 state = modest_mail_operation_clone_state (self);
1329 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1330 g_slice_free (ModestMailOperationState, state);
1335 transfer_folder_cb (TnyFolder *folder,
1336 TnyFolderStore *into,
1338 TnyFolder *new_folder, GError **err,
1341 ModestMailOperation *self = NULL;
1342 ModestMailOperationPrivate *priv = NULL;
1344 self = MODEST_MAIL_OPERATION (user_data);
1346 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1349 priv->error = g_error_copy (*err);
1351 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1352 } else if (cancelled) {
1353 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1354 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1355 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1356 _("Transference of %s was cancelled."),
1357 tny_folder_get_name (folder));
1360 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1364 g_object_unref (folder);
1365 g_object_unref (into);
1366 if (new_folder != NULL)
1367 g_object_unref (new_folder);
1369 /* Notify about operation end */
1370 modest_mail_operation_notify_end (self);
1374 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1376 TnyFolderStore *parent,
1377 gboolean delete_original)
1379 ModestMailOperationPrivate *priv = NULL;
1380 ModestTnyFolderRules parent_rules, rules;
1382 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1383 g_return_if_fail (TNY_IS_FOLDER (folder));
1384 g_return_if_fail (TNY_IS_FOLDER (parent));
1386 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1388 /* Get account and set it into mail_operation */
1389 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1390 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1392 /* Get folder rules */
1393 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1394 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1396 if (!TNY_IS_FOLDER_STORE (parent)) {
1400 /* The moveable restriction is applied also to copy operation */
1401 if ((!TNY_IS_FOLDER_STORE (parent)) || (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1402 /* Set status failed and set an error */
1403 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1404 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1405 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1406 _("mail_in_ui_folder_move_target_error"));
1408 /* Notify the queue */
1409 modest_mail_operation_notify_end (self);
1410 } else if (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1411 /* Set status failed and set an error */
1412 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1413 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1414 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1415 _("FIXME: parent folder does not accept new folders"));
1417 /* Notify the queue */
1418 modest_mail_operation_notify_end (self);
1420 /* Pick references for async calls */
1421 g_object_ref (folder);
1422 g_object_ref (parent);
1424 /* Move/Copy folder */
1425 tny_folder_copy_async (folder,
1427 tny_folder_get_name (folder),
1430 transfer_folder_status_cb,
1436 modest_mail_operation_rename_folder (ModestMailOperation *self,
1440 ModestMailOperationPrivate *priv;
1441 ModestTnyFolderRules rules;
1443 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1444 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1445 g_return_if_fail (name);
1447 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1449 /* Get account and set it into mail_operation */
1450 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1452 /* Check folder rules */
1453 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1454 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1455 /* Set status failed and set an error */
1456 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1457 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1458 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1459 _("FIXME: unable to rename"));
1461 /* Notify about operation end */
1462 modest_mail_operation_notify_end (self);
1464 /* Rename. Camel handles folder subscription/unsubscription */
1465 TnyFolderStore *into;
1467 into = tny_folder_get_folder_store (folder);
1468 tny_folder_copy_async (folder, into, name, TRUE,
1470 transfer_folder_status_cb,
1473 g_object_unref (into);
1478 /* ******************************************************************* */
1479 /* ************************** MSG ACTIONS ************************* */
1480 /* ******************************************************************* */
1482 void modest_mail_operation_get_msg (ModestMailOperation *self,
1484 GetMsgAsyncUserCallback user_callback,
1487 GetMsgAsyncHelper *helper = NULL;
1489 ModestMailOperationPrivate *priv;
1491 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1492 g_return_if_fail (TNY_IS_HEADER (header));
1494 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1495 folder = tny_header_get_folder (header);
1497 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1499 /* Get message from folder */
1501 /* Get account and set it into mail_operation */
1502 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1504 helper = g_slice_new0 (GetMsgAsyncHelper);
1505 helper->mail_op = self;
1506 helper->user_callback = user_callback;
1507 helper->user_data = user_data;
1508 helper->header = g_object_ref (header);
1510 tny_folder_get_msg_async (folder, header, get_msg_cb, get_msg_status_cb, helper);
1512 g_object_unref (G_OBJECT (folder));
1514 /* Set status failed and set an error */
1515 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1516 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1517 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1518 _("Error trying to get a message. No folder found for header"));
1520 /* Notify the queue */
1521 modest_mail_operation_notify_end (self);
1526 get_msg_cb (TnyFolder *folder,
1532 GetMsgAsyncHelper *helper = NULL;
1533 ModestMailOperation *self = NULL;
1534 ModestMailOperationPrivate *priv = NULL;
1536 helper = (GetMsgAsyncHelper *) user_data;
1537 g_return_if_fail (helper != NULL);
1538 self = helper->mail_op;
1539 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
1540 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1542 /* Check errors and cancel */
1544 priv->error = g_error_copy (*error);
1545 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1549 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1550 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1551 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1552 _("Error trying to refresh the contents of %s"),
1553 tny_folder_get_name (folder));
1557 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1559 /* If user defined callback function was defined, call it */
1560 if (helper->user_callback) {
1561 helper->user_callback (self, helper->header, msg, helper->user_data);
1566 g_object_unref (helper->header);
1567 g_slice_free (GetMsgAsyncHelper, helper);
1569 /* Notify about operation end */
1570 modest_mail_operation_notify_end (self);
1574 get_msg_status_cb (GObject *obj,
1578 GetMsgAsyncHelper *helper = NULL;
1579 ModestMailOperation *self;
1580 ModestMailOperationPrivate *priv;
1581 ModestMailOperationState *state;
1583 g_return_if_fail (status != NULL);
1584 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
1586 helper = (GetMsgAsyncHelper *) user_data;
1587 g_return_if_fail (helper != NULL);
1589 self = helper->mail_op;
1590 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1592 if ((status->position == 1) && (status->of_total == 100))
1598 state = modest_mail_operation_clone_state (self);
1599 state->bytes_done = status->position;
1600 state->bytes_total = status->of_total;
1601 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1602 g_slice_free (ModestMailOperationState, state);
1605 /****************************************************/
1607 ModestMailOperation *mail_op;
1609 GetMsgAsyncUserCallback user_callback;
1611 GDestroyNotify notify;
1615 GetMsgAsyncUserCallback user_callback;
1619 ModestMailOperation *mail_op;
1620 } NotifyGetMsgsInfo;
1624 * Used by get_msgs_full_thread to call the user_callback for each
1625 * message that has been read
1628 notify_get_msgs_full (gpointer data)
1630 NotifyGetMsgsInfo *info;
1632 info = (NotifyGetMsgsInfo *) data;
1634 /* Call the user callback */
1635 info->user_callback (info->mail_op, info->header, info->msg, info->user_data);
1637 g_slice_free (NotifyGetMsgsInfo, info);
1643 * Used by get_msgs_full_thread to free al the thread resources and to
1644 * call the destroy function for the passed user_data
1647 get_msgs_full_destroyer (gpointer data)
1649 GetFullMsgsInfo *info;
1651 info = (GetFullMsgsInfo *) data;
1654 info->notify (info->user_data);
1657 g_object_unref (info->headers);
1658 g_slice_free (GetFullMsgsInfo, info);
1664 get_msgs_full_thread (gpointer thr_user_data)
1666 GetFullMsgsInfo *info;
1667 ModestMailOperationPrivate *priv = NULL;
1668 TnyIterator *iter = NULL;
1670 info = (GetFullMsgsInfo *) thr_user_data;
1671 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1673 iter = tny_list_create_iterator (info->headers);
1674 while (!tny_iterator_is_done (iter)) {
1678 header = TNY_HEADER (tny_iterator_get_current (iter));
1679 folder = tny_header_get_folder (header);
1681 /* Get message from folder */
1684 /* The callback will call it per each header */
1685 msg = tny_folder_get_msg (folder, header, &(priv->error));
1688 ModestMailOperationState *state;
1693 /* notify progress */
1694 state = modest_mail_operation_clone_state (info->mail_op);
1695 pair = modest_pair_new (g_object_ref (info->mail_op), state, FALSE);
1696 g_idle_add_full (G_PRIORITY_HIGH_IDLE, idle_notify_progress_once,
1697 pair, (GDestroyNotify) modest_pair_free);
1699 /* The callback is the responsible for
1700 freeing the message */
1701 if (info->user_callback) {
1702 NotifyGetMsgsInfo *info_notify;
1703 info_notify = g_slice_new0 (NotifyGetMsgsInfo);
1704 info_notify->user_callback = info->user_callback;
1705 info_notify->mail_op = info->mail_op;
1706 info_notify->header = g_object_ref (header);
1707 info_notify->msg = g_object_ref (msg);
1708 info_notify->user_data = info->user_data;
1709 g_idle_add_full (G_PRIORITY_HIGH_IDLE,
1710 notify_get_msgs_full,
1713 g_object_unref (msg);
1716 /* Set status failed and set an error */
1717 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1718 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1719 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1720 "Error trying to get a message. No folder found for header");
1722 g_object_unref (header);
1723 tny_iterator_next (iter);
1726 /* Set operation status */
1727 if (priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
1728 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1730 /* Notify about operation end */
1731 g_idle_add (notify_update_account_queue, g_object_ref (info->mail_op));
1733 /* Free thread resources. Will be called after all previous idles */
1734 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1, get_msgs_full_destroyer, info, NULL);
1740 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
1741 TnyList *header_list,
1742 GetMsgAsyncUserCallback user_callback,
1744 GDestroyNotify notify)
1746 TnyHeader *header = NULL;
1747 TnyFolder *folder = NULL;
1749 ModestMailOperationPrivate *priv = NULL;
1750 GetFullMsgsInfo *info = NULL;
1751 gboolean size_ok = TRUE;
1753 TnyIterator *iter = NULL;
1755 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1757 /* Init mail operation */
1758 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1759 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1761 priv->total = tny_list_get_length(header_list);
1763 /* Get account and set it into mail_operation */
1764 if (tny_list_get_length (header_list) >= 1) {
1765 iter = tny_list_create_iterator (header_list);
1766 header = TNY_HEADER (tny_iterator_get_current (iter));
1767 folder = tny_header_get_folder (header);
1768 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1769 g_object_unref (header);
1770 g_object_unref (folder);
1772 if (tny_list_get_length (header_list) == 1) {
1773 g_object_unref (iter);
1778 /* Get msg size limit */
1779 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1780 MODEST_CONF_MSG_SIZE_LIMIT,
1783 g_clear_error (&(priv->error));
1784 max_size = G_MAXINT;
1786 max_size = max_size * KB;
1789 /* Check message size limits. If there is only one message
1790 always retrieve it */
1792 while (!tny_iterator_is_done (iter) && size_ok) {
1793 header = TNY_HEADER (tny_iterator_get_current (iter));
1794 if (tny_header_get_message_size (header) >= max_size)
1796 g_object_unref (header);
1797 tny_iterator_next (iter);
1799 g_object_unref (iter);
1803 /* Create the info */
1804 info = g_slice_new0 (GetFullMsgsInfo);
1805 info->mail_op = self;
1806 info->user_callback = user_callback;
1807 info->user_data = user_data;
1808 info->headers = g_object_ref (header_list);
1809 info->notify = notify;
1811 thread = g_thread_create (get_msgs_full_thread, info, FALSE, NULL);
1813 /* Set status failed and set an error */
1814 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1815 /* FIXME: the error msg is different for pop */
1816 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1817 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
1818 _("emev_ni_ui_imap_msg_size_exceed_error"));
1819 /* Remove from queue and free resources */
1820 modest_mail_operation_notify_end (self);
1828 modest_mail_operation_remove_msg (ModestMailOperation *self,
1830 gboolean remove_to_trash)
1833 ModestMailOperationPrivate *priv;
1835 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1836 g_return_if_fail (TNY_IS_HEADER (header));
1838 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1839 folder = tny_header_get_folder (header);
1841 /* Get account and set it into mail_operation */
1842 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1844 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1846 /* Delete or move to trash */
1847 if (remove_to_trash) {
1848 TnyFolder *trash_folder;
1849 TnyStoreAccount *store_account;
1851 store_account = TNY_STORE_ACCOUNT (modest_tny_folder_get_account (folder));
1852 trash_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT(store_account),
1853 TNY_FOLDER_TYPE_TRASH);
1858 headers = tny_simple_list_new ();
1859 tny_list_append (headers, G_OBJECT (header));
1860 g_object_unref (header);
1863 modest_mail_operation_xfer_msgs (self, headers, trash_folder, TRUE, NULL, NULL);
1864 g_object_unref (headers);
1865 /* g_object_unref (trash_folder); */
1867 ModestMailOperationPrivate *priv;
1869 /* Set status failed and set an error */
1870 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1871 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1872 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1873 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1874 _("Error trying to delete a message. Trash folder not found"));
1877 g_object_unref (G_OBJECT (store_account));
1879 tny_folder_remove_msg (folder, header, &(priv->error));
1881 tny_folder_sync(folder, TRUE, &(priv->error));
1886 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1888 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1891 g_object_unref (G_OBJECT (folder));
1893 /* Notify about operation end */
1894 modest_mail_operation_notify_end (self);
1898 transfer_msgs_status_cb (GObject *obj,
1902 XFerMsgAsyncHelper *helper = NULL;
1903 ModestMailOperation *self;
1904 ModestMailOperationPrivate *priv;
1905 ModestMailOperationState *state;
1908 g_return_if_fail (status != NULL);
1909 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
1911 helper = (XFerMsgAsyncHelper *) user_data;
1912 g_return_if_fail (helper != NULL);
1914 self = helper->mail_op;
1915 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1917 if ((status->position == 1) && (status->of_total == 100))
1920 priv->done = status->position;
1921 priv->total = status->of_total;
1923 state = modest_mail_operation_clone_state (self);
1924 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1925 g_slice_free (ModestMailOperationState, state);
1930 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError **err, gpointer user_data)
1932 XFerMsgAsyncHelper *helper;
1933 ModestMailOperation *self;
1934 ModestMailOperationPrivate *priv;
1936 helper = (XFerMsgAsyncHelper *) user_data;
1937 self = helper->mail_op;
1939 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1942 priv->error = g_error_copy (*err);
1944 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1945 } else if (cancelled) {
1946 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1947 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1948 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1949 _("Error trying to refresh the contents of %s"),
1950 tny_folder_get_name (folder));
1953 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1956 /* If user defined callback function was defined, call it */
1957 if (helper->user_callback) {
1958 helper->user_callback (priv->source, helper->user_data);
1962 g_object_unref (helper->headers);
1963 g_object_unref (helper->dest_folder);
1964 g_object_unref (helper->mail_op);
1965 g_slice_free (XFerMsgAsyncHelper, helper);
1966 g_object_unref (folder);
1968 /* Notify about operation end */
1969 modest_mail_operation_notify_end (self);
1973 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
1976 gboolean delete_original,
1977 XferMsgsAsynUserCallback user_callback,
1980 ModestMailOperationPrivate *priv;
1982 TnyFolder *src_folder;
1983 XFerMsgAsyncHelper *helper;
1985 ModestTnyFolderRules rules;
1987 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1988 g_return_if_fail (TNY_IS_LIST (headers));
1989 g_return_if_fail (TNY_IS_FOLDER (folder));
1991 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1994 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1996 /* Apply folder rules */
1997 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1999 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2000 /* Set status failed and set an error */
2001 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2002 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2003 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2004 _("FIXME: folder does not accept msgs"));
2005 /* Notify the queue */
2006 modest_mail_operation_notify_end (self);
2010 /* Create the helper */
2011 helper = g_slice_new0 (XFerMsgAsyncHelper);
2012 helper->mail_op = g_object_ref(self);
2013 helper->dest_folder = g_object_ref(folder);
2014 helper->headers = g_object_ref(headers);
2015 helper->user_callback = user_callback;
2016 helper->user_data = user_data;
2018 /* Get source folder */
2019 iter = tny_list_create_iterator (headers);
2020 header = TNY_HEADER (tny_iterator_get_current (iter));
2021 src_folder = tny_header_get_folder (header);
2022 g_object_unref (header);
2023 g_object_unref (iter);
2025 /* Get account and set it into mail_operation */
2026 priv->account = modest_tny_folder_get_account (src_folder);
2028 /* Transfer messages */
2029 tny_folder_transfer_msgs_async (src_folder,
2034 transfer_msgs_status_cb,
2040 on_refresh_folder (TnyFolder *folder,
2045 RefreshAsyncHelper *helper = NULL;
2046 ModestMailOperation *self = NULL;
2047 ModestMailOperationPrivate *priv = NULL;
2049 helper = (RefreshAsyncHelper *) user_data;
2050 self = helper->mail_op;
2051 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2054 priv->error = g_error_copy (*error);
2055 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2060 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2061 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2062 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2063 _("Error trying to refresh the contents of %s"),
2064 tny_folder_get_name (folder));
2068 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2071 /* Call user defined callback, if it exists */
2072 if (helper->user_callback)
2073 helper->user_callback (priv->source, folder, helper->user_data);
2076 g_object_unref (helper->mail_op);
2077 g_slice_free (RefreshAsyncHelper, helper);
2078 g_object_unref (folder);
2080 /* Notify about operation end */
2081 modest_mail_operation_notify_end (self);
2085 on_refresh_folder_status_update (GObject *obj,
2089 RefreshAsyncHelper *helper = NULL;
2090 ModestMailOperation *self = NULL;
2091 ModestMailOperationPrivate *priv = NULL;
2092 ModestMailOperationState *state;
2094 g_return_if_fail (user_data != NULL);
2095 g_return_if_fail (status != NULL);
2096 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2098 helper = (RefreshAsyncHelper *) user_data;
2099 self = helper->mail_op;
2100 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2102 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2104 priv->done = status->position;
2105 priv->total = status->of_total;
2107 state = modest_mail_operation_clone_state (self);
2108 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2109 g_slice_free (ModestMailOperationState, state);
2113 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2115 RefreshAsyncUserCallback user_callback,
2118 ModestMailOperationPrivate *priv = NULL;
2119 RefreshAsyncHelper *helper = NULL;
2121 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2123 /* Pick a reference */
2124 g_object_ref (folder);
2126 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2128 /* Get account and set it into mail_operation */
2129 priv->account = modest_tny_folder_get_account (folder);
2131 /* Create the helper */
2132 helper = g_slice_new0 (RefreshAsyncHelper);
2133 helper->mail_op = g_object_ref(self);
2134 helper->user_callback = user_callback;
2135 helper->user_data = user_data;
2137 /* Refresh the folder. TODO: tinymail could issue a status
2138 updates before the callback call then this could happen. We
2139 must review the design */
2140 tny_folder_refresh_async (folder,
2142 on_refresh_folder_status_update,
2148 * It's used by the mail operation queue to notify the observers
2149 * attached to that signal that the operation finished. We need to use
2150 * that because tinymail does not give us the progress of a given
2151 * operation when it finishes (it directly calls the operation
2155 modest_mail_operation_notify_end (ModestMailOperation *self)
2157 ModestMailOperationState *state;
2158 ModestMailOperationPrivate *priv = NULL;
2160 g_return_if_fail (self);
2162 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2165 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
2169 /* Set the account back to not busy */
2170 if (priv->account_name) {
2171 modest_account_mgr_set_account_busy(modest_runtime_get_account_mgr(), priv->account_name,
2173 g_free(priv->account_name);
2174 priv->account_name = NULL;
2177 /* Notify the observers about the mail opertation end */
2178 state = modest_mail_operation_clone_state (self);
2179 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2180 g_slice_free (ModestMailOperationState, state);