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-camel-pop-folder.h>
39 #include <tny-camel-imap-folder.h>
40 #include <tny-camel-mem-stream.h>
41 #include <tny-simple-list.h>
42 #include <tny-send-queue.h>
43 #include <tny-status.h>
44 #include <tny-folder-observer.h>
45 #include <camel/camel-stream-mem.h>
46 #include <glib/gi18n.h>
47 #include "modest-platform.h"
48 #include "modest-account-mgr-helpers.h"
49 #include <modest-tny-account.h>
50 #include <modest-tny-send-queue.h>
51 #include <modest-runtime.h>
52 #include "modest-text-utils.h"
53 #include "modest-tny-msg.h"
54 #include "modest-tny-folder.h"
55 #include "modest-tny-account-store.h"
56 #include "modest-tny-platform-factory.h"
57 #include "modest-marshal.h"
58 #include "modest-error.h"
59 #include "modest-mail-operation.h"
64 * Remove all these #ifdef stuff when the tinymail's idle calls become
67 #define TINYMAIL_IDLES_NOT_LOCKED_YET 1
69 /* 'private'/'protected' functions */
70 static void modest_mail_operation_class_init (ModestMailOperationClass *klass);
71 static void modest_mail_operation_init (ModestMailOperation *obj);
72 static void modest_mail_operation_finalize (GObject *obj);
74 static void get_msg_async_cb (TnyFolder *folder,
80 static void get_msg_status_cb (GObject *obj,
84 static void modest_mail_operation_notify_start (ModestMailOperation *self);
85 static void modest_mail_operation_notify_end (ModestMailOperation *self);
87 static void notify_progress_of_multiple_messages (ModestMailOperation *self,
89 gint *last_total_bytes,
90 gint *sum_total_bytes,
92 gboolean increment_done);
94 static guint compute_message_list_size (TnyList *headers);
96 static guint compute_message_array_size (GPtrArray *headers);
98 static int compare_headers_by_date (gconstpointer a,
101 enum _ModestMailOperationSignals
103 PROGRESS_CHANGED_SIGNAL,
104 OPERATION_STARTED_SIGNAL,
105 OPERATION_FINISHED_SIGNAL,
109 typedef struct _ModestMailOperationPrivate ModestMailOperationPrivate;
110 struct _ModestMailOperationPrivate {
116 ErrorCheckingUserCallback error_checking;
117 gpointer error_checking_user_data;
118 ErrorCheckingUserDataDestroyer error_checking_user_data_destroyer;
119 ModestMailOperationStatus status;
120 ModestMailOperationTypeOperation op_type;
123 #define MODEST_MAIL_OPERATION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
124 MODEST_TYPE_MAIL_OPERATION, \
125 ModestMailOperationPrivate))
127 #define CHECK_EXCEPTION(priv, new_status) if (priv->error) {\
128 priv->status = new_status;\
133 GetMsgAsyncUserCallback user_callback;
136 ModestMailOperation *mail_op;
137 GDestroyNotify destroy_notify;
138 gint last_total_bytes;
139 gint sum_total_bytes;
144 ModestMailOperation *mail_op;
146 gulong msg_sent_handler;
147 gulong error_happened_handler;
150 static void send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
151 guint nth, guint total, gpointer userdata);
152 static void send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
153 GError *error, gpointer userdata);
154 static void common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg,
157 typedef struct _RefreshAsyncHelper {
158 ModestMailOperation *mail_op;
159 RefreshAsyncUserCallback user_callback;
161 } RefreshAsyncHelper;
163 typedef struct _XFerMsgAsyncHelper
165 ModestMailOperation *mail_op;
167 TnyFolder *dest_folder;
168 XferAsyncUserCallback user_callback;
171 gint last_total_bytes;
172 gint sum_total_bytes;
174 } XFerMsgAsyncHelper;
176 typedef void (*ModestMailOperationCreateMsgCallback) (ModestMailOperation *mail_op,
180 static void modest_mail_operation_create_msg (ModestMailOperation *self,
181 const gchar *from, const gchar *to,
182 const gchar *cc, const gchar *bcc,
183 const gchar *subject, const gchar *plain_body,
184 const gchar *html_body, const GList *attachments_list,
185 const GList *images_list,
186 TnyHeaderFlags priority_flags,
187 ModestMailOperationCreateMsgCallback callback,
190 static gboolean idle_notify_queue (gpointer data);
193 ModestMailOperation *mail_op;
201 GList *attachments_list;
203 TnyHeaderFlags priority_flags;
204 ModestMailOperationCreateMsgCallback callback;
210 ModestMailOperation *mail_op;
212 ModestMailOperationCreateMsgCallback callback;
217 static GObjectClass *parent_class = NULL;
219 static guint signals[NUM_SIGNALS] = {0};
222 modest_mail_operation_get_type (void)
224 static GType my_type = 0;
226 static const GTypeInfo my_info = {
227 sizeof(ModestMailOperationClass),
228 NULL, /* base init */
229 NULL, /* base finalize */
230 (GClassInitFunc) modest_mail_operation_class_init,
231 NULL, /* class finalize */
232 NULL, /* class data */
233 sizeof(ModestMailOperation),
235 (GInstanceInitFunc) modest_mail_operation_init,
238 my_type = g_type_register_static (G_TYPE_OBJECT,
239 "ModestMailOperation",
246 modest_mail_operation_class_init (ModestMailOperationClass *klass)
248 GObjectClass *gobject_class;
249 gobject_class = (GObjectClass*) klass;
251 parent_class = g_type_class_peek_parent (klass);
252 gobject_class->finalize = modest_mail_operation_finalize;
254 g_type_class_add_private (gobject_class, sizeof(ModestMailOperationPrivate));
257 * ModestMailOperation::progress-changed
258 * @self: the #MailOperation that emits the signal
259 * @user_data: user data set when the signal handler was connected
261 * Emitted when the progress of a mail operation changes
263 signals[PROGRESS_CHANGED_SIGNAL] =
264 g_signal_new ("progress-changed",
265 G_TYPE_FROM_CLASS (gobject_class),
267 G_STRUCT_OFFSET (ModestMailOperationClass, progress_changed),
269 g_cclosure_marshal_VOID__POINTER,
270 G_TYPE_NONE, 1, G_TYPE_POINTER);
274 * This signal is issued whenever a mail operation starts, and
275 * starts mean when the tinymail operation is issued. This
276 * means that it could happen that something wrong happens and
277 * the tinymail function is never called. In this situation a
278 * operation-finished will be issued without any
281 signals[OPERATION_STARTED_SIGNAL] =
282 g_signal_new ("operation-started",
283 G_TYPE_FROM_CLASS (gobject_class),
285 G_STRUCT_OFFSET (ModestMailOperationClass, operation_started),
287 g_cclosure_marshal_VOID__VOID,
292 * This signal is issued whenever a mail operation
293 * finishes. Note that this signal could be issued without any
294 * previous "operation-started" signal, because this last one
295 * is only issued when the tinymail operation is successfully
298 signals[OPERATION_FINISHED_SIGNAL] =
299 g_signal_new ("operation-finished",
300 G_TYPE_FROM_CLASS (gobject_class),
302 G_STRUCT_OFFSET (ModestMailOperationClass, operation_finished),
304 g_cclosure_marshal_VOID__VOID,
309 modest_mail_operation_init (ModestMailOperation *obj)
311 ModestMailOperationPrivate *priv;
313 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
315 priv->account = NULL;
316 priv->status = MODEST_MAIL_OPERATION_STATUS_INVALID;
317 priv->op_type = MODEST_MAIL_OPERATION_TYPE_UNKNOWN;
322 priv->error_checking = NULL;
323 priv->error_checking_user_data = NULL;
327 modest_mail_operation_finalize (GObject *obj)
329 ModestMailOperationPrivate *priv;
331 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
336 g_error_free (priv->error);
340 g_object_unref (priv->source);
344 g_object_unref (priv->account);
345 priv->account = NULL;
349 G_OBJECT_CLASS(parent_class)->finalize (obj);
353 modest_mail_operation_new (GObject *source)
355 ModestMailOperation *obj;
356 ModestMailOperationPrivate *priv;
358 obj = MODEST_MAIL_OPERATION(g_object_new(MODEST_TYPE_MAIL_OPERATION, NULL));
359 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
362 priv->source = g_object_ref(source);
368 modest_mail_operation_new_with_error_handling (GObject *source,
369 ErrorCheckingUserCallback error_handler,
371 ErrorCheckingUserDataDestroyer error_handler_destroyer)
373 ModestMailOperation *obj;
374 ModestMailOperationPrivate *priv;
376 obj = modest_mail_operation_new (source);
377 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(obj);
379 g_return_val_if_fail (error_handler != NULL, obj);
380 priv->error_checking = error_handler;
381 priv->error_checking_user_data = user_data;
382 priv->error_checking_user_data_destroyer = error_handler_destroyer;
388 modest_mail_operation_execute_error_handler (ModestMailOperation *self)
390 ModestMailOperationPrivate *priv;
392 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION(self));
394 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
395 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
397 /* Call the user callback */
398 if (priv->error_checking != NULL)
399 priv->error_checking (self, priv->error_checking_user_data);
403 ModestMailOperationTypeOperation
404 modest_mail_operation_get_type_operation (ModestMailOperation *self)
406 ModestMailOperationPrivate *priv;
408 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
409 MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
411 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
413 return priv->op_type;
417 modest_mail_operation_is_mine (ModestMailOperation *self,
420 ModestMailOperationPrivate *priv;
422 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
425 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
426 if (priv->source == NULL) return FALSE;
428 return priv->source == me;
432 modest_mail_operation_get_source (ModestMailOperation *self)
434 ModestMailOperationPrivate *priv;
436 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
439 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
441 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
445 return (priv->source) ? g_object_ref (priv->source) : NULL;
448 ModestMailOperationStatus
449 modest_mail_operation_get_status (ModestMailOperation *self)
451 ModestMailOperationPrivate *priv;
453 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
454 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
455 MODEST_MAIL_OPERATION_STATUS_INVALID);
457 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
459 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
460 return MODEST_MAIL_OPERATION_STATUS_INVALID;
467 modest_mail_operation_get_error (ModestMailOperation *self)
469 ModestMailOperationPrivate *priv;
471 g_return_val_if_fail (self, NULL);
472 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
474 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
477 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
485 modest_mail_operation_cancel (ModestMailOperation *self)
487 ModestMailOperationPrivate *priv;
488 gboolean canceled = FALSE;
490 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION (self), FALSE);
492 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
494 /* Note that if we call cancel with an already canceled mail
495 operation the progress changed signal won't be emitted */
496 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
500 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
502 /* Cancel the mail operation. We need to wrap it between this
503 start/stop operations to allow following calls to the
505 g_return_val_if_fail (priv->account, FALSE);
507 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
508 ModestTnySendQueue *queue;
509 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account));
510 /* Cancel sending without removing the item */
511 tny_send_queue_cancel (TNY_SEND_QUEUE (queue), FALSE, NULL);
513 /* Cancel operation */
514 tny_account_cancel (priv->account);
521 modest_mail_operation_get_task_done (ModestMailOperation *self)
523 ModestMailOperationPrivate *priv;
525 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
528 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
533 modest_mail_operation_get_task_total (ModestMailOperation *self)
535 ModestMailOperationPrivate *priv;
537 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
540 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
545 modest_mail_operation_is_finished (ModestMailOperation *self)
547 ModestMailOperationPrivate *priv;
548 gboolean retval = FALSE;
550 g_return_val_if_fail (self && MODEST_IS_MAIL_OPERATION(self),
553 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
555 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
556 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
557 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
558 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
568 * Creates an image of the current state of a mail operation, the
569 * caller must free it
571 static ModestMailOperationState *
572 modest_mail_operation_clone_state (ModestMailOperation *self)
574 ModestMailOperationState *state;
575 ModestMailOperationPrivate *priv;
577 /* FIXME: this should be fixed properly
579 * in some cases, priv was NULL, so checking here to
582 g_return_val_if_fail (self, NULL);
583 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
584 g_return_val_if_fail (priv, NULL);
589 state = g_slice_new (ModestMailOperationState);
591 state->status = priv->status;
592 state->op_type = priv->op_type;
593 state->done = priv->done;
594 state->total = priv->total;
595 state->finished = modest_mail_operation_is_finished (self);
596 state->bytes_done = 0;
597 state->bytes_total = 0;
602 /* ******************************************************************* */
603 /* ************************** SEND ACTIONS ************************* */
604 /* ******************************************************************* */
607 modest_mail_operation_send_mail (ModestMailOperation *self,
608 TnyTransportAccount *transport_account,
611 TnySendQueue *send_queue = NULL;
612 ModestMailOperationPrivate *priv;
615 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
616 g_return_if_fail (transport_account && TNY_IS_TRANSPORT_ACCOUNT (transport_account));
617 g_return_if_fail (msg && TNY_IS_MSG (msg));
619 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
621 /* Get account and set it into mail_operation */
622 priv->account = g_object_ref (transport_account);
623 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
627 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
628 if (!TNY_IS_SEND_QUEUE(send_queue)) {
629 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
630 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
631 "modest: could not find send queue for account\n");
632 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
633 modest_mail_operation_notify_end (self);
636 /* Add the msg to the queue */
637 modest_mail_operation_notify_start (self);
638 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue),
642 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
644 info = g_slice_new0 (SendMsgInfo);
646 info->mail_op = g_object_ref (self);
647 info->msg = g_object_ref (msg);
648 info->msg_sent_handler = g_signal_connect (G_OBJECT (send_queue), "msg-sent",
649 G_CALLBACK (send_mail_msg_sent_handler), info);
650 info->error_happened_handler = g_signal_connect (G_OBJECT (send_queue), "error-happened",
651 G_CALLBACK (send_mail_error_happened_handler), info);
657 common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg,
660 g_signal_handler_disconnect (queue, info->msg_sent_handler);
661 g_signal_handler_disconnect (queue, info->error_happened_handler);
663 g_object_unref (info->msg);
664 modest_mail_operation_notify_end (info->mail_op);
665 g_object_unref (info->mail_op);
667 g_slice_free (SendMsgInfo, info);
671 send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
672 guint nth, guint total, gpointer userdata)
674 SendMsgInfo *info = (SendMsgInfo *) userdata;
675 TnyHeader *hdr1, *hdr2;
676 const char *msgid1, *msgid2;
677 hdr1 = tny_msg_get_header(msg);
678 hdr2 = tny_msg_get_header(info->msg);
679 msgid1 = tny_header_get_message_id(hdr1);
680 msgid2 = tny_header_get_message_id(hdr2);
681 if (msgid1 == NULL) msgid1 = "(null)";
682 if (msgid2 == NULL) msgid2 = "(null)";
684 if (!strcmp (msgid1, msgid2)) {
685 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
686 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
688 common_send_mail_operation_end (queue, msg, info);
690 g_object_unref(G_OBJECT(hdr1));
691 g_object_unref(G_OBJECT(hdr2));
695 send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
696 GError *error, gpointer userdata)
698 SendMsgInfo *info = (SendMsgInfo *) userdata;
699 TnyHeader *hdr1, *hdr2;
700 hdr1 = tny_msg_get_header(msg);
701 hdr2 = tny_msg_get_header(info->msg);
702 const char *msgid1, *msgid2;
703 hdr1 = tny_msg_get_header(msg);
704 hdr2 = tny_msg_get_header(info->msg);
705 msgid1 = tny_header_get_message_id(hdr1);
706 msgid2 = tny_header_get_message_id(hdr2);
707 if (msgid1 == NULL) msgid1 = "(null)";
708 if (msgid2 == NULL) msgid2 = "(null)";
710 if (!strcmp (msgid1, msgid2)) {
711 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
712 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
713 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
714 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
715 "modest: send mail failed\n");
717 common_send_mail_operation_end (queue, msg, info);
719 g_object_unref(G_OBJECT(hdr1));
720 g_object_unref(G_OBJECT(hdr2));
725 idle_create_msg_cb (gpointer idle_data)
727 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
729 /* This is a GDK lock because we are an idle callback and
730 * info->callback can contain Gtk+ code */
732 gdk_threads_enter (); /* CHECKED */
733 info->callback (info->mail_op, info->msg, info->userdata);
735 g_object_unref (info->mail_op);
737 g_object_unref (info->msg);
738 g_slice_free (CreateMsgIdleInfo, info);
739 gdk_threads_leave (); /* CHECKED */
745 create_msg_thread (gpointer thread_data)
747 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
748 TnyMsg *new_msg = NULL;
749 ModestMailOperationPrivate *priv;
751 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
752 if (info->html_body == NULL) {
753 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
754 info->bcc, info->subject, info->plain_body,
755 info->attachments_list);
757 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
758 info->bcc, info->subject, info->html_body,
759 info->plain_body, info->attachments_list,
766 /* Set priority flags in message */
767 header = tny_msg_get_header (new_msg);
768 tny_header_set_flag (header, info->priority_flags);
770 /* Set attachment flags in message */
771 if (info->attachments_list != NULL)
772 tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
774 g_object_unref (G_OBJECT(header));
776 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
777 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
778 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
779 "modest: failed to create a new msg\n");
787 g_free (info->plain_body);
788 g_free (info->html_body);
789 g_free (info->subject);
790 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
791 g_list_free (info->attachments_list);
792 g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
793 g_list_free (info->images_list);
795 if (info->callback) {
796 CreateMsgIdleInfo *idle_info;
797 idle_info = g_slice_new0 (CreateMsgIdleInfo);
798 idle_info->mail_op = g_object_ref (info->mail_op);
799 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
800 idle_info->callback = info->callback;
801 idle_info->userdata = info->userdata;
802 g_idle_add (idle_create_msg_cb, idle_info);
804 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
807 g_object_unref (info->mail_op);
808 g_slice_free (CreateMsgInfo, info);
813 modest_mail_operation_create_msg (ModestMailOperation *self,
814 const gchar *from, const gchar *to,
815 const gchar *cc, const gchar *bcc,
816 const gchar *subject, const gchar *plain_body,
817 const gchar *html_body,
818 const GList *attachments_list,
819 const GList *images_list,
820 TnyHeaderFlags priority_flags,
821 ModestMailOperationCreateMsgCallback callback,
824 CreateMsgInfo *info = NULL;
826 info = g_slice_new0 (CreateMsgInfo);
827 info->mail_op = g_object_ref (self);
829 info->from = g_strdup (from);
830 info->to = g_strdup (to);
831 info->cc = g_strdup (cc);
832 info->bcc = g_strdup (bcc);
833 info->subject = g_strdup (subject);
834 info->plain_body = g_strdup (plain_body);
835 info->html_body = g_strdup (html_body);
836 info->attachments_list = g_list_copy ((GList *) attachments_list);
837 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
838 info->images_list = g_list_copy ((GList *) images_list);
839 g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
840 info->priority_flags = priority_flags;
842 info->callback = callback;
843 info->userdata = userdata;
845 g_thread_create (create_msg_thread, info, FALSE, NULL);
850 TnyTransportAccount *transport_account;
855 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
859 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
860 TnyFolder *draft_folder = NULL;
861 TnyFolder *outbox_folder = NULL;
869 /* Call mail operation */
870 modest_mail_operation_send_mail (self, info->transport_account, msg);
872 /* Remove old mail from its source folder */
873 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
874 TNY_FOLDER_TYPE_DRAFTS);
875 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
876 TNY_FOLDER_TYPE_OUTBOX);
877 if (info->draft_msg != NULL) {
878 TnyFolder *folder = NULL;
879 TnyFolder *src_folder = NULL;
880 TnyFolderType folder_type;
881 folder = tny_msg_get_folder (info->draft_msg);
882 if (folder == NULL) goto end;
883 folder_type = modest_tny_folder_guess_folder_type (folder);
885 if (folder_type == TNY_FOLDER_TYPE_INVALID)
886 g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
888 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
889 src_folder = outbox_folder;
891 src_folder = draft_folder;
893 /* Note: This can fail (with a warning) if the message is not really already in a folder,
894 * because this function requires it to have a UID. */
895 header = tny_msg_get_header (info->draft_msg);
896 tny_folder_remove_msg (src_folder, header, NULL);
898 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
899 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
901 g_object_unref (header);
902 g_object_unref (folder);
909 g_object_unref (info->draft_msg);
911 g_object_unref (draft_folder);
913 g_object_unref (outbox_folder);
914 if (info->transport_account)
915 g_object_unref (info->transport_account);
916 g_slice_free (SendNewMailInfo, info);
920 modest_mail_operation_send_new_mail (ModestMailOperation *self,
921 TnyTransportAccount *transport_account,
923 const gchar *from, const gchar *to,
924 const gchar *cc, const gchar *bcc,
925 const gchar *subject, const gchar *plain_body,
926 const gchar *html_body,
927 const GList *attachments_list,
928 const GList *images_list,
929 TnyHeaderFlags priority_flags)
931 ModestMailOperationPrivate *priv = NULL;
932 SendNewMailInfo *info;
934 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
935 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
937 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
938 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
939 priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
940 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
942 /* Check parametters */
944 /* Set status failed and set an error */
945 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
946 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
947 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
948 _("Error trying to send a mail. You need to set at least one recipient"));
951 info = g_slice_new0 (SendNewMailInfo);
952 info->transport_account = transport_account;
953 if (transport_account)
954 g_object_ref (transport_account);
955 info->draft_msg = draft_msg;
957 g_object_ref (draft_msg);
960 modest_mail_operation_notify_start (self);
961 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
962 attachments_list, images_list, priority_flags,
963 modest_mail_operation_send_new_mail_cb, info);
969 TnyTransportAccount *transport_account;
971 SaveToDraftstCallback callback;
975 ModestMailOperation *mailop;
976 } SaveToDraftsAddMsgInfo;
979 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
984 ModestMailOperationPrivate *priv = NULL;
985 SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
987 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
990 g_warning ("%s: priv->error != NULL", __FUNCTION__);
991 g_error_free(priv->error);
994 priv->error = (err == NULL) ? NULL : g_error_copy(err);
996 if ((!priv->error) && (info->draft_msg != NULL)) {
997 TnyHeader *header = tny_msg_get_header (info->draft_msg);
998 TnyFolder *src_folder = tny_header_get_folder (header);
1000 /* Remove the old draft */
1001 tny_folder_remove_msg (src_folder, header, NULL);
1003 /* Synchronize to expunge and to update the msg counts */
1004 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1005 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1007 g_object_unref (G_OBJECT(header));
1008 g_object_unref (G_OBJECT(src_folder));
1012 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1014 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1016 /* Call the user callback */
1018 info->callback (info->mailop, info->msg, info->user_data);
1020 if (info->transport_account)
1021 g_object_unref (G_OBJECT(info->transport_account));
1022 if (info->draft_msg)
1023 g_object_unref (G_OBJECT (info->draft_msg));
1025 g_object_unref (G_OBJECT(info->drafts));
1027 g_object_unref (G_OBJECT (info->msg));
1028 g_slice_free (SaveToDraftsAddMsgInfo, info);
1030 modest_mail_operation_notify_end (info->mailop);
1031 g_object_unref(info->mailop);
1036 TnyTransportAccount *transport_account;
1038 SaveToDraftstCallback callback;
1043 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1047 TnyFolder *drafts = NULL;
1048 ModestMailOperationPrivate *priv = NULL;
1049 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1051 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1054 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1055 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1056 "modest: failed to create a new msg\n");
1058 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1059 TNY_FOLDER_TYPE_DRAFTS);
1061 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1062 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1063 "modest: failed to create a new msg\n");
1068 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1069 cb_info->transport_account = g_object_ref(info->transport_account);
1070 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1071 cb_info->callback = info->callback;
1072 cb_info->user_data = info->user_data;
1073 cb_info->drafts = g_object_ref(drafts);
1074 cb_info->msg = g_object_ref(msg);
1075 cb_info->mailop = g_object_ref(self);
1076 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1079 /* Call the user callback */
1080 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1082 info->callback (self, msg, info->user_data);
1083 modest_mail_operation_notify_end (self);
1087 g_object_unref (G_OBJECT(drafts));
1088 if (info->draft_msg)
1089 g_object_unref (G_OBJECT (info->draft_msg));
1090 if (info->transport_account)
1091 g_object_unref (G_OBJECT(info->transport_account));
1092 g_slice_free (SaveToDraftsInfo, info);
1096 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1097 TnyTransportAccount *transport_account,
1099 const gchar *from, const gchar *to,
1100 const gchar *cc, const gchar *bcc,
1101 const gchar *subject, const gchar *plain_body,
1102 const gchar *html_body,
1103 const GList *attachments_list,
1104 const GList *images_list,
1105 TnyHeaderFlags priority_flags,
1106 SaveToDraftstCallback callback,
1109 ModestMailOperationPrivate *priv = NULL;
1110 SaveToDraftsInfo *info = NULL;
1112 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1113 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1115 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1117 /* Get account and set it into mail_operation */
1118 priv->account = g_object_ref (transport_account);
1119 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1121 info = g_slice_new0 (SaveToDraftsInfo);
1122 info->transport_account = g_object_ref (transport_account);
1123 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1124 info->callback = callback;
1125 info->user_data = user_data;
1127 modest_mail_operation_notify_start (self);
1128 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1129 attachments_list, images_list, priority_flags,
1130 modest_mail_operation_save_to_drafts_cb, info);
1135 ModestMailOperation *mail_op;
1136 TnyMimePart *mime_part;
1138 GetMimePartSizeCallback callback;
1140 } GetMimePartSizeInfo;
1142 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
1143 /* We use this folder observer to track the headers that have been
1144 * added to a folder */
1147 TnyList *new_headers;
1148 } InternalFolderObserver;
1151 GObjectClass parent;
1152 } InternalFolderObserverClass;
1154 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1156 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1157 internal_folder_observer,
1159 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1163 foreach_add_item (gpointer header, gpointer user_data)
1165 tny_list_prepend (TNY_LIST (user_data),
1166 g_object_ref (G_OBJECT (header)));
1169 /* This is the method that looks for new messages in a folder */
1171 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1173 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1175 TnyFolderChangeChanged changed;
1177 changed = tny_folder_change_get_changed (change);
1179 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1182 /* Get added headers */
1183 list = tny_simple_list_new ();
1184 tny_folder_change_get_added_headers (change, list);
1186 /* Add them to the folder observer */
1187 tny_list_foreach (list, foreach_add_item,
1188 derived->new_headers);
1190 g_object_unref (G_OBJECT (list));
1195 internal_folder_observer_init (InternalFolderObserver *self)
1197 self->new_headers = tny_simple_list_new ();
1200 internal_folder_observer_finalize (GObject *object)
1202 InternalFolderObserver *self;
1204 self = (InternalFolderObserver *) object;
1205 g_object_unref (self->new_headers);
1207 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1210 tny_folder_observer_init (TnyFolderObserverIface *iface)
1212 iface->update_func = internal_folder_observer_update;
1215 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1217 GObjectClass *object_class;
1219 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1220 object_class = (GObjectClass*) klass;
1221 object_class->finalize = internal_folder_observer_finalize;
1226 ModestMailOperation *mail_op;
1227 gchar *account_name;
1228 UpdateAccountCallback callback;
1233 TnyFolderObserver *inbox_observer;
1234 guint update_timeout;
1235 } UpdateAccountInfo;
1239 destroy_update_account_info (UpdateAccountInfo *info)
1241 if (info->update_timeout) {
1242 g_source_remove (info->update_timeout);
1243 info->update_timeout = 0;
1246 g_free (info->account_name);
1247 g_object_unref (info->folders);
1248 g_object_unref (info->mail_op);
1249 g_slice_free (UpdateAccountInfo, info);
1253 update_account_get_msg_async_cb (TnyFolder *folder,
1259 GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1261 /* Just delete the helper. Don't do anything with the new
1262 msg. There is also no need to check for errors */
1263 g_object_unref (msg_info->mail_op);
1264 g_object_unref (msg_info->header);
1265 g_slice_free (GetMsgInfo, msg_info);
1270 inbox_refreshed_cb (TnyFolder *inbox,
1275 UpdateAccountInfo *info;
1276 ModestMailOperationPrivate *priv;
1277 TnyIterator *new_headers_iter;
1278 GPtrArray *new_headers_array = NULL;
1279 gint max_size, retrieve_limit, i;
1280 ModestAccountMgr *mgr;
1281 ModestAccountRetrieveType retrieve_type;
1282 TnyList *new_headers = NULL;
1283 gboolean headers_only;
1284 TnyTransportAccount *transport_account;
1285 ModestTnySendQueue *send_queue;
1287 info = (UpdateAccountInfo *) user_data;
1288 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1289 mgr = modest_runtime_get_account_mgr ();
1291 if (canceled || err || !inbox) {
1292 /* Try to send anyway */
1296 /* Get the message max size */
1297 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1298 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1300 max_size = G_MAXINT;
1302 max_size = max_size * KB;
1304 /* Create the new headers array. We need it to sort the
1305 new headers by date */
1306 new_headers_array = g_ptr_array_new ();
1307 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1308 while (!tny_iterator_is_done (new_headers_iter)) {
1309 TnyHeader *header = NULL;
1311 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1312 /* Apply per-message size limits */
1313 if (tny_header_get_message_size (header) < max_size)
1314 g_ptr_array_add (new_headers_array, g_object_ref (header));
1316 g_object_unref (header);
1317 tny_iterator_next (new_headers_iter);
1319 g_object_unref (new_headers_iter);
1320 tny_folder_remove_observer (inbox, info->inbox_observer);
1321 g_object_unref (info->inbox_observer);
1322 info->inbox_observer = NULL;
1324 /* Update the last updated key, even if we don't have to get new headers */
1325 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1327 if (new_headers_array->len == 0)
1330 /* Get per-account message amount retrieval limit */
1331 retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1332 if (retrieve_limit == 0)
1333 retrieve_limit = G_MAXINT;
1335 /* Get per-account retrieval type */
1336 retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1337 headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1340 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1342 /* TODO: Ask the user, instead of just failing,
1343 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1344 * all' and 'Newest only' buttons. */
1345 if (new_headers_array->len > retrieve_limit) {
1349 if (!headers_only) {
1351 const gint msg_list_size = compute_message_array_size (new_headers_array);
1354 priv->total = MIN (new_headers_array->len, retrieve_limit);
1355 while (msg_num < priv->total) {
1356 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1357 TnyFolder *folder = tny_header_get_folder (header);
1358 GetMsgInfo *msg_info;
1360 /* Create the message info */
1361 msg_info = g_slice_new0 (GetMsgInfo);
1362 msg_info->mail_op = g_object_ref (info->mail_op);
1363 msg_info->header = g_object_ref (header);
1364 msg_info->total_bytes = msg_list_size;
1366 /* Get message in an async way */
1367 tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1368 get_msg_status_cb, msg_info);
1370 g_object_unref (folder);
1376 /* Copy the headers to a list and free the array */
1377 new_headers = tny_simple_list_new ();
1378 for (i=0; i < new_headers_array->len; i++) {
1379 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1380 tny_list_append (new_headers, G_OBJECT (header));
1382 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1383 g_ptr_array_free (new_headers_array, FALSE);
1390 /* Get the transport account */
1391 transport_account = (TnyTransportAccount *)
1392 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1393 info->account_name);
1396 send_queue = modest_runtime_get_send_queue (transport_account);
1397 modest_tny_send_queue_try_to_send (send_queue);
1399 /* Check if the operation was a success */
1401 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1403 /* Set the account back to not busy */
1404 modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1406 /* Call the user callback */
1408 info->callback (info->mail_op, new_headers, info->user_data);
1410 /* Notify about operation end */
1411 modest_mail_operation_notify_end (info->mail_op);
1415 g_object_unref (new_headers);
1416 destroy_update_account_info (info);
1420 recurse_folders_async_cb (TnyFolderStore *folder_store,
1426 UpdateAccountInfo *info;
1427 ModestMailOperationPrivate *priv;
1429 info = (UpdateAccountInfo *) user_data;
1430 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1432 if (err || canceled) {
1433 /* Try to continue anyway */
1435 TnyIterator *iter = tny_list_create_iterator (list);
1436 while (!tny_iterator_is_done (iter)) {
1437 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1438 TnyList *folders = tny_simple_list_new ();
1440 /* Add to the list of all folders */
1441 tny_list_append (info->folders, (GObject *) folder);
1443 /* Add pending call */
1444 info->pending_calls++;
1446 tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb,
1449 g_object_unref (G_OBJECT (folder));
1451 tny_iterator_next (iter);
1453 g_object_unref (G_OBJECT (iter));
1454 g_object_unref (G_OBJECT (list));
1457 /* Remove my own pending call */
1458 info->pending_calls--;
1460 /* This means that we have all the folders */
1461 if (info->pending_calls == 0) {
1462 TnyIterator *iter_all_folders;
1463 TnyFolder *inbox = NULL;
1465 iter_all_folders = tny_list_create_iterator (info->folders);
1467 /* Do a poke status over all folders */
1468 while (!tny_iterator_is_done (iter_all_folders) &&
1469 priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1470 TnyFolder *folder = NULL;
1472 folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1474 if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1475 /* Get a reference to the INBOX */
1476 inbox = g_object_ref (folder);
1478 /* Issue a poke status over the folder */
1480 tny_folder_poke_status (folder);
1483 /* Free and go to next */
1484 g_object_unref (folder);
1485 tny_iterator_next (iter_all_folders);
1487 g_object_unref (iter_all_folders);
1489 /* Stop the progress notification */
1490 g_source_remove (info->update_timeout);
1491 info->update_timeout = 0;
1493 /* Refresh the INBOX */
1495 /* Refresh the folder. Our observer receives
1496 * the new emails during folder refreshes, so
1497 * we can use observer->new_headers
1499 info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1500 tny_folder_add_observer (inbox, info->inbox_observer);
1502 /* Refresh the INBOX */
1503 tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1504 g_object_unref (inbox);
1506 /* We could not perform the inbox refresh but
1507 we'll try to send mails anyway */
1508 inbox_refreshed_cb (inbox, FALSE, NULL, info);
1514 * Issues the "progress-changed" signal. The timer won't be removed,
1515 * so you must call g_source_remove to stop the signal emission
1518 timeout_notify_progress (gpointer data)
1520 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1521 ModestMailOperationState *state;
1523 state = modest_mail_operation_clone_state (mail_op);
1525 /* This is a GDK lock because we are an idle callback and
1526 * the handlers of this signal can contain Gtk+ code */
1528 gdk_threads_enter (); /* CHECKED */
1529 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1530 gdk_threads_leave (); /* CHECKED */
1532 g_slice_free (ModestMailOperationState, state);
1538 modest_mail_operation_update_account (ModestMailOperation *self,
1539 const gchar *account_name,
1541 UpdateAccountCallback callback,
1544 UpdateAccountInfo *info = NULL;
1545 ModestMailOperationPrivate *priv = NULL;
1546 ModestTnyAccountStore *account_store = NULL;
1547 TnyStoreAccount *store_account = NULL;
1550 /* Init mail operation */
1551 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1554 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1555 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1557 /* Get the store account */
1558 account_store = modest_runtime_get_account_store ();
1559 store_account = (TnyStoreAccount *)
1560 modest_tny_account_store_get_server_account (account_store,
1562 TNY_ACCOUNT_TYPE_STORE);
1563 priv->account = g_object_ref (store_account);
1565 /* Create the helper object */
1566 info = g_slice_new0 (UpdateAccountInfo);
1567 info->pending_calls = 1;
1568 info->folders = tny_simple_list_new ();
1569 info->mail_op = g_object_ref (self);
1570 info->poke_all = poke_all;
1571 info->account_name = g_strdup (account_name);
1572 info->callback = callback;
1573 info->user_data = user_data;
1574 info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1576 /* Set account busy */
1577 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1578 modest_mail_operation_notify_start (self);
1580 /* Get all folders and continue in the callback */
1581 folders = tny_simple_list_new ();
1582 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1583 folders, recurse_folders_async_cb,
1588 * Used to notify the queue from the main
1589 * loop. We call it inside an idle call to achieve that
1592 idle_notify_queue (gpointer data)
1594 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1596 gdk_threads_enter ();
1597 modest_mail_operation_notify_end (mail_op);
1598 gdk_threads_leave ();
1599 g_object_unref (mail_op);
1605 compare_headers_by_date (gconstpointer a,
1608 TnyHeader **header1, **header2;
1609 time_t sent1, sent2;
1611 header1 = (TnyHeader **) a;
1612 header2 = (TnyHeader **) b;
1614 sent1 = tny_header_get_date_sent (*header1);
1615 sent2 = tny_header_get_date_sent (*header2);
1617 /* We want the most recent ones (greater time_t) at the
1626 /* ******************************************************************* */
1627 /* ************************** STORE ACTIONS ************************* */
1628 /* ******************************************************************* */
1632 modest_mail_operation_create_folder (ModestMailOperation *self,
1633 TnyFolderStore *parent,
1636 ModestMailOperationPrivate *priv;
1637 TnyFolder *new_folder = NULL;
1639 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1640 g_return_val_if_fail (name, NULL);
1642 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1643 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1644 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1645 g_object_ref (parent) :
1646 modest_tny_folder_get_account (TNY_FOLDER (parent));
1648 /* Check for already existing folder */
1649 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1650 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1651 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1652 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1653 _CS("ckdg_ib_folder_already_exists"));
1657 if (TNY_IS_FOLDER (parent)) {
1658 /* Check folder rules */
1659 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1660 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1661 /* Set status failed and set an error */
1662 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1663 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1664 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1665 _("mail_in_ui_folder_create_error"));
1669 if (!strcmp (name, " ") || strchr (name, '/')) {
1670 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1671 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1672 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1673 _("mail_in_ui_folder_create_error"));
1677 /* Create the folder */
1678 modest_mail_operation_notify_start (self);
1679 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1680 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1682 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1685 /* Notify about operation end */
1686 modest_mail_operation_notify_end (self);
1692 modest_mail_operation_remove_folder (ModestMailOperation *self,
1694 gboolean remove_to_trash)
1696 TnyAccount *account;
1697 ModestMailOperationPrivate *priv;
1698 ModestTnyFolderRules rules;
1700 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1701 g_return_if_fail (TNY_IS_FOLDER (folder));
1703 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1705 /* Check folder rules */
1706 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1707 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1708 /* Set status failed and set an error */
1709 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1710 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1711 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1712 _("mail_in_ui_folder_delete_error"));
1716 /* Get the account */
1717 account = modest_tny_folder_get_account (folder);
1718 priv->account = g_object_ref(account);
1719 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1721 /* Delete folder or move to trash */
1722 if (remove_to_trash) {
1723 TnyFolder *trash_folder = NULL;
1724 trash_folder = modest_tny_account_get_special_folder (account,
1725 TNY_FOLDER_TYPE_TRASH);
1726 /* TODO: error_handling */
1728 modest_mail_operation_notify_start (self);
1729 modest_mail_operation_xfer_folder (self, folder,
1730 TNY_FOLDER_STORE (trash_folder),
1732 g_object_unref (trash_folder);
1735 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1737 modest_mail_operation_notify_start (self);
1738 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1739 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1742 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1744 g_object_unref (parent);
1746 g_warning ("%s: could not get parent folder", __FUNCTION__);
1748 g_object_unref (G_OBJECT (account));
1751 /* Notify about operation end */
1752 modest_mail_operation_notify_end (self);
1756 transfer_folder_status_cb (GObject *obj,
1760 ModestMailOperation *self;
1761 ModestMailOperationPrivate *priv;
1762 ModestMailOperationState *state;
1763 XFerMsgAsyncHelper *helper;
1765 g_return_if_fail (status != NULL);
1766 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1768 helper = (XFerMsgAsyncHelper *) user_data;
1769 g_return_if_fail (helper != NULL);
1771 self = helper->mail_op;
1772 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1774 priv->done = status->position;
1775 priv->total = status->of_total;
1777 state = modest_mail_operation_clone_state (self);
1779 /* This is not a GDK lock because we are a Tinymail callback
1780 * which is already GDK locked by Tinymail */
1782 /* no gdk_threads_enter (), CHECKED */
1784 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1786 /* no gdk_threads_leave (), CHECKED */
1788 g_slice_free (ModestMailOperationState, state);
1793 transfer_folder_cb (TnyFolder *folder,
1795 TnyFolderStore *into,
1796 TnyFolder *new_folder,
1800 XFerMsgAsyncHelper *helper;
1801 ModestMailOperation *self = NULL;
1802 ModestMailOperationPrivate *priv = NULL;
1804 helper = (XFerMsgAsyncHelper *) user_data;
1805 g_return_if_fail (helper != NULL);
1807 self = helper->mail_op;
1808 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1811 priv->error = g_error_copy (err);
1813 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1814 } else if (cancelled) {
1815 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1816 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1817 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1818 _("Transference of %s was cancelled."),
1819 tny_folder_get_name (folder));
1822 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1825 /* Notify about operation end */
1826 modest_mail_operation_notify_end (self);
1828 /* If user defined callback function was defined, call it */
1829 if (helper->user_callback) {
1831 /* This is not a GDK lock because we are a Tinymail callback
1832 * which is already GDK locked by Tinymail */
1834 /* no gdk_threads_enter (), CHECKED */
1835 helper->user_callback (self, helper->user_data);
1836 /* no gdk_threads_leave () , CHECKED */
1840 g_object_unref (helper->mail_op);
1841 g_slice_free (XFerMsgAsyncHelper, helper);
1846 * This function checks if the new name is a valid name for our local
1847 * folders account. The new name could not be the same than then name
1848 * of any of the mandatory local folders
1850 * We can not rely on tinymail because tinymail does not check the
1851 * name of the virtual folders that the account could have in the case
1852 * that we're doing a rename (because it directly calls Camel which
1853 * knows nothing about our virtual folders).
1855 * In the case of an actual copy/move (i.e. move/copy a folder between
1856 * accounts) tinymail uses the tny_folder_store_create_account which
1857 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1858 * checks the new name of the folder, so this call in that case
1859 * wouldn't be needed. *But* NOTE that if tinymail changes its
1860 * implementation (if folder transfers within the same account is no
1861 * longer implemented as a rename) this call will allow Modest to work
1864 * If the new name is not valid, this function will set the status to
1865 * failed and will set also an error in the mail operation
1868 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1869 TnyFolderStore *into,
1870 const gchar *new_name)
1872 if (TNY_IS_ACCOUNT (into) &&
1873 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1874 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1876 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1877 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1878 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1879 _CS("ckdg_ib_folder_already_exists"));
1886 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1888 TnyFolderStore *parent,
1889 gboolean delete_original,
1890 XferAsyncUserCallback user_callback,
1893 ModestMailOperationPrivate *priv = NULL;
1894 ModestTnyFolderRules parent_rules = 0, rules;
1895 XFerMsgAsyncHelper *helper = NULL;
1896 const gchar *folder_name = NULL;
1897 const gchar *error_msg;
1899 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1900 g_return_if_fail (TNY_IS_FOLDER (folder));
1901 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1903 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1904 folder_name = tny_folder_get_name (folder);
1906 /* Set the error msg */
1907 error_msg = _("mail_in_ui_folder_move_target_error");
1909 /* Get account and set it into mail_operation */
1910 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1911 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1912 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1914 /* Get folder rules */
1915 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1916 if (TNY_IS_FOLDER (parent))
1917 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1919 /* Apply operation constraints */
1920 if ((gpointer) parent == (gpointer) folder ||
1921 (!TNY_IS_FOLDER_STORE (parent)) ||
1922 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1925 } else if (TNY_IS_FOLDER (parent) &&
1926 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1930 } else if (TNY_IS_FOLDER (parent) &&
1931 TNY_IS_FOLDER_STORE (folder) &&
1932 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1933 TNY_FOLDER_STORE (folder))) {
1934 /* Do not move a parent into a child */
1936 } else if (TNY_IS_FOLDER_STORE (parent) &&
1937 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1938 /* Check that the new folder name is not used by any
1941 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1942 /* Check that the new folder name is not used by any
1943 special local folder */
1946 /* Create the helper */
1947 helper = g_slice_new0 (XFerMsgAsyncHelper);
1948 helper->mail_op = g_object_ref (self);
1949 helper->dest_folder = NULL;
1950 helper->headers = NULL;
1951 helper->user_callback = user_callback;
1952 helper->user_data = user_data;
1954 /* Move/Copy folder */
1955 modest_mail_operation_notify_start (self);
1956 tny_folder_copy_async (folder,
1958 tny_folder_get_name (folder),
1961 transfer_folder_status_cb,
1967 /* Set status failed and set an error */
1968 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1969 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1970 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1973 /* Call the user callback if exists */
1975 user_callback (self, user_data);
1977 /* Notify the queue */
1978 modest_mail_operation_notify_end (self);
1982 modest_mail_operation_rename_folder (ModestMailOperation *self,
1986 ModestMailOperationPrivate *priv;
1987 ModestTnyFolderRules rules;
1988 XFerMsgAsyncHelper *helper;
1990 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1991 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1992 g_return_if_fail (name);
1994 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1996 /* Get account and set it into mail_operation */
1997 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1998 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2000 /* Check folder rules */
2001 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2002 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
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: unable to rename"));
2009 /* Notify about operation end */
2010 modest_mail_operation_notify_end (self);
2011 } else if (!strcmp (name, " ") || strchr (name, '/')) {
2012 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2013 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2014 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2015 _("FIXME: unable to rename"));
2016 /* Notify about operation end */
2017 modest_mail_operation_notify_end (self);
2019 TnyFolderStore *into;
2021 into = tny_folder_get_folder_store (folder);
2023 /* Check that the new folder name is not used by any
2024 special local folder */
2025 if (new_name_valid_if_local_account (priv, into, name)) {
2026 /* Create the helper */
2027 helper = g_slice_new0 (XFerMsgAsyncHelper);
2028 helper->mail_op = g_object_ref(self);
2029 helper->dest_folder = NULL;
2030 helper->headers = NULL;
2031 helper->user_callback = NULL;
2032 helper->user_data = NULL;
2034 /* Rename. Camel handles folder subscription/unsubscription */
2035 modest_mail_operation_notify_start (self);
2036 tny_folder_copy_async (folder, into, name, TRUE,
2038 transfer_folder_status_cb,
2041 modest_mail_operation_notify_end (self);
2043 g_object_unref (into);
2047 /* ******************************************************************* */
2048 /* ************************** MSG ACTIONS ************************* */
2049 /* ******************************************************************* */
2052 modest_mail_operation_get_msg (ModestMailOperation *self,
2054 GetMsgAsyncUserCallback user_callback,
2057 GetMsgInfo *helper = NULL;
2059 ModestMailOperationPrivate *priv;
2061 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2062 g_return_if_fail (TNY_IS_HEADER (header));
2064 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2065 folder = tny_header_get_folder (header);
2067 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2071 /* Get account and set it into mail_operation */
2072 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2074 /* Check for cached messages */
2075 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2076 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2078 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2080 /* Create the helper */
2081 helper = g_slice_new0 (GetMsgInfo);
2082 helper->header = g_object_ref (header);
2083 helper->mail_op = g_object_ref (self);
2084 helper->user_callback = user_callback;
2085 helper->user_data = user_data;
2086 helper->destroy_notify = NULL;
2087 helper->last_total_bytes = 0;
2088 helper->sum_total_bytes = 0;
2089 helper->total_bytes = tny_header_get_message_size (header);
2091 modest_mail_operation_notify_start (self);
2092 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2094 g_object_unref (G_OBJECT (folder));
2098 get_msg_status_cb (GObject *obj,
2102 GetMsgInfo *helper = NULL;
2104 g_return_if_fail (status != NULL);
2105 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2107 helper = (GetMsgInfo *) user_data;
2108 g_return_if_fail (helper != NULL);
2110 /* Notify progress */
2111 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2112 &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2116 get_msg_async_cb (TnyFolder *folder,
2122 GetMsgInfo *info = NULL;
2123 ModestMailOperationPrivate *priv = NULL;
2126 info = (GetMsgInfo *) user_data;
2128 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2130 finished = (priv->done == priv->total) ? TRUE : FALSE;
2133 if (canceled || err) {
2134 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2136 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2137 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2140 /* Set the success status before calling the user callback */
2141 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2142 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2146 /* Call the user callback */
2147 if (info->user_callback)
2148 info->user_callback (info->mail_op, info->header, canceled,
2149 msg, err, info->user_data);
2151 /* Notify about operation end if this is the last callback */
2153 /* Free user data */
2154 if (info->destroy_notify)
2155 info->destroy_notify (info->user_data);
2157 /* Notify about operation end */
2158 modest_mail_operation_notify_end (info->mail_op);
2162 g_object_unref (info->header);
2163 g_object_unref (info->mail_op);
2164 g_slice_free (GetMsgInfo, info);
2168 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2169 TnyList *header_list,
2170 GetMsgAsyncUserCallback user_callback,
2172 GDestroyNotify notify)
2174 ModestMailOperationPrivate *priv = NULL;
2175 gboolean size_ok = TRUE;
2177 TnyIterator *iter = NULL;
2179 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2181 /* Init mail operation */
2182 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2183 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2184 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2186 priv->total = tny_list_get_length(header_list);
2188 /* Get account and set it into mail_operation */
2189 if (tny_list_get_length (header_list) >= 1) {
2190 iter = tny_list_create_iterator (header_list);
2191 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2193 TnyFolder *folder = tny_header_get_folder (header);
2195 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2196 g_object_unref (folder);
2198 g_object_unref (header);
2201 if (tny_list_get_length (header_list) == 1) {
2202 g_object_unref (iter);
2207 /* Get msg size limit */
2208 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2209 MODEST_CONF_MSG_SIZE_LIMIT,
2212 g_clear_error (&(priv->error));
2213 max_size = G_MAXINT;
2215 max_size = max_size * KB;
2218 /* Check message size limits. If there is only one message
2219 always retrieve it */
2221 while (!tny_iterator_is_done (iter) && size_ok) {
2222 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2224 if (tny_header_get_message_size (header) >= max_size)
2226 g_object_unref (header);
2229 tny_iterator_next (iter);
2231 g_object_unref (iter);
2235 const gint msg_list_size = compute_message_list_size (header_list);
2237 modest_mail_operation_notify_start (self);
2238 iter = tny_list_create_iterator (header_list);
2239 while (!tny_iterator_is_done (iter)) {
2240 GetMsgInfo *msg_info = NULL;
2241 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2242 TnyFolder *folder = tny_header_get_folder (header);
2244 /* Create the message info */
2245 msg_info = g_slice_new0 (GetMsgInfo);
2246 msg_info->mail_op = g_object_ref (self);
2247 msg_info->header = g_object_ref (header);
2248 msg_info->user_callback = user_callback;
2249 msg_info->user_data = user_data;
2250 msg_info->destroy_notify = notify;
2251 msg_info->last_total_bytes = 0;
2252 msg_info->sum_total_bytes = 0;
2253 msg_info->total_bytes = msg_list_size;
2255 /* The callback will call it per each header */
2256 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2258 /* Free and go on */
2259 g_object_unref (header);
2260 g_object_unref (folder);
2261 tny_iterator_next (iter);
2263 g_object_unref (iter);
2265 /* Set status failed and set an error */
2266 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2267 /* FIXME: the error msg is different for pop */
2268 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2269 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2270 _("emev_ni_ui_imap_msg_size_exceed_error"));
2271 /* Remove from queue and free resources */
2272 modest_mail_operation_notify_end (self);
2280 modest_mail_operation_remove_msg (ModestMailOperation *self,
2282 gboolean remove_to_trash /*ignored*/)
2285 ModestMailOperationPrivate *priv;
2287 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2288 g_return_if_fail (TNY_IS_HEADER (header));
2290 if (remove_to_trash)
2291 g_warning ("remove to trash is not implemented");
2293 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2294 folder = tny_header_get_folder (header);
2296 /* Get account and set it into mail_operation */
2297 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2298 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2299 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2301 /* remove message from folder */
2302 tny_folder_remove_msg (folder, header, &(priv->error));
2304 gboolean expunge, leave_on_server;
2305 const gchar *account_name;
2306 TnyAccount *account;
2308 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2309 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2311 modest_mail_operation_notify_start (self);
2313 /* Get leave on server setting */
2314 account = tny_folder_get_account (folder);
2315 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2317 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2320 if (TNY_IS_CAMEL_POP_FOLDER (folder) && !leave_on_server)
2326 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2329 g_object_unref (account);
2335 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2337 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2340 g_object_unref (G_OBJECT (folder));
2342 /* Notify about operation end */
2343 modest_mail_operation_notify_end (self);
2347 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2349 gboolean remove_to_trash /*ignored*/)
2352 ModestMailOperationPrivate *priv;
2353 TnyIterator *iter = NULL;
2354 TnyHeader *header = NULL;
2355 TnyList *remove_headers = NULL;
2356 TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2358 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2359 g_return_if_fail (TNY_IS_LIST (headers));
2361 if (remove_to_trash)
2362 g_warning ("remove to trash is not implemented");
2364 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2366 remove_headers = g_object_ref(headers);
2368 /* Get folder from first header and sync it */
2369 iter = tny_list_create_iterator (headers);
2370 header = TNY_HEADER (tny_iterator_get_current (iter));
2371 folder = tny_header_get_folder (header);
2373 /* Don't remove messages that are being sent */
2374 if (modest_tny_folder_is_local_folder (folder)) {
2375 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2377 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2378 TnyTransportAccount *traccount = NULL;
2379 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2380 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2382 ModestTnySendQueueStatus status;
2383 ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2384 TnyIterator *iter = tny_list_create_iterator(headers);
2385 g_object_unref(remove_headers);
2386 remove_headers = TNY_LIST(tny_simple_list_new());
2387 while (!tny_iterator_is_done(iter)) {
2389 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2390 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2391 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2392 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2393 tny_list_append(remove_headers, G_OBJECT(hdr));
2395 g_object_unref(hdr);
2397 tny_iterator_next(iter);
2399 g_object_unref(iter);
2400 g_object_unref(traccount);
2404 /* Get account and set it into mail_operation */
2405 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2406 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2407 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2409 /* remove message from folder */
2410 modest_mail_operation_notify_start (self);
2412 tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2414 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2415 TNY_IS_CAMEL_POP_FOLDER (folder))
2416 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2419 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2425 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2427 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2430 g_object_unref (remove_headers);
2431 g_object_unref (header);
2432 g_object_unref (iter);
2433 g_object_unref (G_OBJECT (folder));
2435 /* Notify about operation end */
2436 modest_mail_operation_notify_end (self);
2440 notify_progress_of_multiple_messages (ModestMailOperation *self,
2442 gint *last_total_bytes,
2443 gint *sum_total_bytes,
2445 gboolean increment_done)
2447 ModestMailOperationPrivate *priv;
2448 ModestMailOperationState *state;
2449 gboolean is_num_bytes;
2451 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2453 /* We know that tinymail sends us information about
2454 transferred bytes with this particular message */
2455 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2457 state = modest_mail_operation_clone_state (self);
2458 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2459 /* We know that we're in a different message when the
2460 total number of bytes to transfer is different. Of
2461 course it could fail if we're transferring messages
2462 of the same size, but this is a workarround */
2463 if (status->of_total != *last_total_bytes) {
2464 /* We need to increment the done when there is
2465 no information about each individual
2466 message, we need to do this in message
2467 transfers, and we don't do it for getting
2471 *sum_total_bytes += *last_total_bytes;
2472 *last_total_bytes = status->of_total;
2474 state->bytes_done += status->position + *sum_total_bytes;
2475 state->bytes_total = total_bytes;
2477 /* Notify the status change. Only notify about changes
2478 referred to bytes */
2479 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2483 g_slice_free (ModestMailOperationState, state);
2487 transfer_msgs_status_cb (GObject *obj,
2491 XFerMsgAsyncHelper *helper;
2493 g_return_if_fail (status != NULL);
2494 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2496 helper = (XFerMsgAsyncHelper *) user_data;
2497 g_return_if_fail (helper != NULL);
2499 /* Notify progress */
2500 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2501 &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2506 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2508 XFerMsgAsyncHelper *helper;
2509 ModestMailOperation *self;
2510 ModestMailOperationPrivate *priv;
2511 TnyIterator *iter = NULL;
2512 TnyHeader *header = NULL;
2514 helper = (XFerMsgAsyncHelper *) user_data;
2515 self = helper->mail_op;
2517 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2520 priv->error = g_error_copy (err);
2522 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2523 } else if (cancelled) {
2524 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2525 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2526 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2527 _("Error trying to refresh the contents of %s"),
2528 tny_folder_get_name (folder));
2531 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2533 /* Update folder counts */
2534 tny_folder_poke_status (folder);
2535 tny_folder_poke_status (helper->dest_folder);
2539 /* Mark headers as deleted and seen */
2540 if ((helper->delete) &&
2541 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2542 iter = tny_list_create_iterator (helper->headers);
2543 while (!tny_iterator_is_done (iter)) {
2544 header = TNY_HEADER (tny_iterator_get_current (iter));
2545 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2546 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2547 g_object_unref (header);
2549 tny_iterator_next (iter);
2555 /* Notify about operation end */
2556 modest_mail_operation_notify_end (self);
2558 /* If user defined callback function was defined, call it */
2559 if (helper->user_callback) {
2560 /* This is not a GDK lock because we are a Tinymail callback and
2561 * Tinymail already acquires the Gdk lock */
2563 /* no gdk_threads_enter (), CHECKED */
2564 helper->user_callback (self, helper->user_data);
2565 /* no gdk_threads_leave (), CHECKED */
2569 if (helper->headers)
2570 g_object_unref (helper->headers);
2571 if (helper->dest_folder)
2572 g_object_unref (helper->dest_folder);
2573 if (helper->mail_op)
2574 g_object_unref (helper->mail_op);
2576 g_object_unref (folder);
2578 g_object_unref (iter);
2579 g_slice_free (XFerMsgAsyncHelper, helper);
2583 compute_message_list_size (TnyList *headers)
2588 iter = tny_list_create_iterator (headers);
2589 while (!tny_iterator_is_done (iter)) {
2590 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2591 size += tny_header_get_message_size (header);
2592 g_object_unref (header);
2593 tny_iterator_next (iter);
2595 g_object_unref (iter);
2601 compute_message_array_size (GPtrArray *headers)
2606 for (i = 0; i < headers->len; i++) {
2607 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2608 size += tny_header_get_message_size (header);
2616 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2619 gboolean delete_original,
2620 XferAsyncUserCallback user_callback,
2623 ModestMailOperationPrivate *priv = NULL;
2624 TnyIterator *iter = NULL;
2625 TnyFolder *src_folder = NULL;
2626 XFerMsgAsyncHelper *helper = NULL;
2627 TnyHeader *header = NULL;
2628 ModestTnyFolderRules rules = 0;
2630 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2631 g_return_if_fail (headers && TNY_IS_LIST (headers));
2632 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2634 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2635 priv->total = tny_list_get_length (headers);
2637 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2638 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2640 /* Apply folder rules */
2641 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2642 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2643 /* Set status failed and set an error */
2644 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2645 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2646 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2647 _CS("ckct_ib_unable_to_paste_here"));
2648 /* Notify the queue */
2649 modest_mail_operation_notify_end (self);
2653 /* Get source folder */
2654 iter = tny_list_create_iterator (headers);
2655 header = TNY_HEADER (tny_iterator_get_current (iter));
2657 src_folder = tny_header_get_folder (header);
2658 g_object_unref (header);
2660 g_object_unref (iter);
2662 if (src_folder == NULL) {
2663 /* Notify the queue */
2664 modest_mail_operation_notify_end (self);
2666 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2671 /* Check folder source and destination */
2672 if (src_folder == folder) {
2673 /* Set status failed and set an error */
2674 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2675 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2676 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2677 _("mail_in_ui_folder_copy_target_error"));
2679 /* Notify the queue */
2680 modest_mail_operation_notify_end (self);
2683 g_object_unref (src_folder);
2687 /* Create the helper */
2688 helper = g_slice_new0 (XFerMsgAsyncHelper);
2689 helper->mail_op = g_object_ref(self);
2690 helper->dest_folder = g_object_ref(folder);
2691 helper->headers = g_object_ref(headers);
2692 helper->user_callback = user_callback;
2693 helper->user_data = user_data;
2694 helper->delete = delete_original;
2695 helper->last_total_bytes = 0;
2696 helper->sum_total_bytes = 0;
2697 helper->total_bytes = compute_message_list_size (headers);
2699 /* Get account and set it into mail_operation */
2700 priv->account = modest_tny_folder_get_account (src_folder);
2702 /* Transfer messages */
2703 modest_mail_operation_notify_start (self);
2704 tny_folder_transfer_msgs_async (src_folder,
2709 transfer_msgs_status_cb,
2715 on_refresh_folder (TnyFolder *folder,
2720 RefreshAsyncHelper *helper = NULL;
2721 ModestMailOperation *self = NULL;
2722 ModestMailOperationPrivate *priv = NULL;
2724 helper = (RefreshAsyncHelper *) user_data;
2725 self = helper->mail_op;
2726 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2728 g_return_if_fail(priv!=NULL);
2731 priv->error = g_error_copy (error);
2732 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2737 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2738 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2739 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2740 _("Error trying to refresh the contents of %s"),
2741 tny_folder_get_name (folder));
2745 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2748 /* Call user defined callback, if it exists */
2749 if (helper->user_callback) {
2751 /* This is not a GDK lock because we are a Tinymail callback and
2752 * Tinymail already acquires the Gdk lock */
2753 helper->user_callback (self, folder, helper->user_data);
2757 g_slice_free (RefreshAsyncHelper, helper);
2759 /* Notify about operation end */
2760 modest_mail_operation_notify_end (self);
2761 g_object_unref(self);
2765 on_refresh_folder_status_update (GObject *obj,
2769 RefreshAsyncHelper *helper = NULL;
2770 ModestMailOperation *self = NULL;
2771 ModestMailOperationPrivate *priv = NULL;
2772 ModestMailOperationState *state;
2774 g_return_if_fail (user_data != NULL);
2775 g_return_if_fail (status != NULL);
2776 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2778 helper = (RefreshAsyncHelper *) user_data;
2779 self = helper->mail_op;
2780 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2782 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2784 priv->done = status->position;
2785 priv->total = status->of_total;
2787 state = modest_mail_operation_clone_state (self);
2789 /* This is not a GDK lock because we are a Tinymail callback and
2790 * Tinymail already acquires the Gdk lock */
2791 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2793 g_slice_free (ModestMailOperationState, state);
2797 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2799 RefreshAsyncUserCallback user_callback,
2802 ModestMailOperationPrivate *priv = NULL;
2803 RefreshAsyncHelper *helper = NULL;
2805 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2807 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2809 /* Get account and set it into mail_operation */
2810 priv->account = modest_tny_folder_get_account (folder);
2811 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2813 /* Create the helper */
2814 helper = g_slice_new0 (RefreshAsyncHelper);
2815 helper->mail_op = g_object_ref(self);
2816 helper->user_callback = user_callback;
2817 helper->user_data = user_data;
2819 /* Refresh the folder. TODO: tinymail could issue a status
2820 updates before the callback call then this could happen. We
2821 must review the design */
2822 modest_mail_operation_notify_start (self);
2823 tny_folder_refresh_async (folder,
2825 on_refresh_folder_status_update,
2831 modest_mail_operation_notify_start (ModestMailOperation *self)
2833 ModestMailOperationPrivate *priv = NULL;
2835 g_return_if_fail (self);
2837 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2839 /* Ensure that all the fields are filled correctly */
2840 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2842 /* Notify the observers about the mail operation. We do not
2843 wrapp this emission because we assume that this function is
2844 always called from within the main lock */
2845 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2850 * It's used by the mail operation queue to notify the observers
2851 * attached to that signal that the operation finished. We need to use
2852 * that because tinymail does not give us the progress of a given
2853 * operation when it finishes (it directly calls the operation
2857 modest_mail_operation_notify_end (ModestMailOperation *self)
2859 ModestMailOperationPrivate *priv = NULL;
2861 g_return_if_fail (self);
2863 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2865 /* Notify the observers about the mail operation end. We do
2866 not wrapp this emission because we assume that this
2867 function is always called from within the main lock */
2868 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2870 /* Remove the error user data */
2871 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2872 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2876 modest_mail_operation_get_account (ModestMailOperation *self)
2878 ModestMailOperationPrivate *priv = NULL;
2880 g_return_val_if_fail (self, NULL);
2882 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2884 return (priv->account) ? g_object_ref (priv->account) : NULL;
2888 modest_mail_operation_noop (ModestMailOperation *self)
2890 ModestMailOperationPrivate *priv = NULL;
2892 g_return_if_fail (self);
2894 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2895 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2896 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2900 /* This mail operation does nothing actually */
2901 modest_mail_operation_notify_start (self);
2902 modest_mail_operation_notify_end (self);