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 const char *msgid1, *msgid2;
702 hdr1 = tny_msg_get_header(msg);
703 hdr2 = tny_msg_get_header(info->msg);
704 msgid1 = tny_header_get_message_id(hdr1);
705 msgid2 = tny_header_get_message_id(hdr2);
706 if (msgid1 == NULL) msgid1 = "(null)";
707 if (msgid2 == NULL) msgid2 = "(null)";
709 if (!strcmp (msgid1, msgid2)) {
710 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
711 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
712 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
713 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
714 "modest: send mail failed\n");
716 common_send_mail_operation_end (queue, msg, info);
718 g_object_unref(G_OBJECT(hdr1));
719 g_object_unref(G_OBJECT(hdr2));
724 idle_create_msg_cb (gpointer idle_data)
726 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
728 /* This is a GDK lock because we are an idle callback and
729 * info->callback can contain Gtk+ code */
731 gdk_threads_enter (); /* CHECKED */
732 info->callback (info->mail_op, info->msg, info->userdata);
734 g_object_unref (info->mail_op);
736 g_object_unref (info->msg);
737 g_slice_free (CreateMsgIdleInfo, info);
738 gdk_threads_leave (); /* CHECKED */
744 create_msg_thread (gpointer thread_data)
746 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
747 TnyMsg *new_msg = NULL;
748 ModestMailOperationPrivate *priv;
750 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
751 if (info->html_body == NULL) {
752 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
753 info->bcc, info->subject, info->plain_body,
754 info->attachments_list);
756 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
757 info->bcc, info->subject, info->html_body,
758 info->plain_body, info->attachments_list,
765 /* Set priority flags in message */
766 header = tny_msg_get_header (new_msg);
767 tny_header_set_flag (header, info->priority_flags);
769 /* Set attachment flags in message */
770 if (info->attachments_list != NULL)
771 tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
773 g_object_unref (G_OBJECT(header));
775 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
776 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
777 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
778 "modest: failed to create a new msg\n");
786 g_free (info->plain_body);
787 g_free (info->html_body);
788 g_free (info->subject);
789 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
790 g_list_free (info->attachments_list);
791 g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
792 g_list_free (info->images_list);
794 if (info->callback) {
795 CreateMsgIdleInfo *idle_info;
796 idle_info = g_slice_new0 (CreateMsgIdleInfo);
797 idle_info->mail_op = g_object_ref (info->mail_op);
798 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
799 idle_info->callback = info->callback;
800 idle_info->userdata = info->userdata;
801 g_idle_add (idle_create_msg_cb, idle_info);
803 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
806 g_object_unref (info->mail_op);
807 g_slice_free (CreateMsgInfo, info);
812 modest_mail_operation_create_msg (ModestMailOperation *self,
813 const gchar *from, const gchar *to,
814 const gchar *cc, const gchar *bcc,
815 const gchar *subject, const gchar *plain_body,
816 const gchar *html_body,
817 const GList *attachments_list,
818 const GList *images_list,
819 TnyHeaderFlags priority_flags,
820 ModestMailOperationCreateMsgCallback callback,
823 CreateMsgInfo *info = NULL;
825 info = g_slice_new0 (CreateMsgInfo);
826 info->mail_op = g_object_ref (self);
828 info->from = g_strdup (from);
829 info->to = g_strdup (to);
830 info->cc = g_strdup (cc);
831 info->bcc = g_strdup (bcc);
832 info->subject = g_strdup (subject);
833 info->plain_body = g_strdup (plain_body);
834 info->html_body = g_strdup (html_body);
835 info->attachments_list = g_list_copy ((GList *) attachments_list);
836 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
837 info->images_list = g_list_copy ((GList *) images_list);
838 g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
839 info->priority_flags = priority_flags;
841 info->callback = callback;
842 info->userdata = userdata;
844 g_thread_create (create_msg_thread, info, FALSE, NULL);
849 TnyTransportAccount *transport_account;
854 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
858 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
859 TnyFolder *draft_folder = NULL;
860 TnyFolder *outbox_folder = NULL;
868 /* Call mail operation */
869 modest_mail_operation_send_mail (self, info->transport_account, msg);
871 /* Remove old mail from its source folder */
872 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
873 TNY_FOLDER_TYPE_DRAFTS);
874 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
875 TNY_FOLDER_TYPE_OUTBOX);
876 if (info->draft_msg != NULL) {
877 TnyFolder *folder = NULL;
878 TnyFolder *src_folder = NULL;
879 TnyFolderType folder_type;
880 folder = tny_msg_get_folder (info->draft_msg);
881 if (folder == NULL) goto end;
882 folder_type = modest_tny_folder_guess_folder_type (folder);
884 if (folder_type == TNY_FOLDER_TYPE_INVALID)
885 g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
887 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
888 src_folder = outbox_folder;
890 src_folder = draft_folder;
892 /* Note: This can fail (with a warning) if the message is not really already in a folder,
893 * because this function requires it to have a UID. */
894 header = tny_msg_get_header (info->draft_msg);
895 tny_folder_remove_msg (src_folder, header, NULL);
897 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
898 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
900 g_object_unref (header);
901 g_object_unref (folder);
908 g_object_unref (info->draft_msg);
910 g_object_unref (draft_folder);
912 g_object_unref (outbox_folder);
913 if (info->transport_account)
914 g_object_unref (info->transport_account);
915 g_slice_free (SendNewMailInfo, info);
919 modest_mail_operation_send_new_mail (ModestMailOperation *self,
920 TnyTransportAccount *transport_account,
922 const gchar *from, const gchar *to,
923 const gchar *cc, const gchar *bcc,
924 const gchar *subject, const gchar *plain_body,
925 const gchar *html_body,
926 const GList *attachments_list,
927 const GList *images_list,
928 TnyHeaderFlags priority_flags)
930 ModestMailOperationPrivate *priv = NULL;
931 SendNewMailInfo *info;
933 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
934 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
936 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
937 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
938 priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
939 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
941 /* Check parametters */
943 /* Set status failed and set an error */
944 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
945 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
946 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
947 _("Error trying to send a mail. You need to set at least one recipient"));
950 info = g_slice_new0 (SendNewMailInfo);
951 info->transport_account = transport_account;
952 if (transport_account)
953 g_object_ref (transport_account);
954 info->draft_msg = draft_msg;
956 g_object_ref (draft_msg);
959 modest_mail_operation_notify_start (self);
960 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
961 attachments_list, images_list, priority_flags,
962 modest_mail_operation_send_new_mail_cb, info);
968 TnyTransportAccount *transport_account;
970 SaveToDraftstCallback callback;
974 ModestMailOperation *mailop;
975 } SaveToDraftsAddMsgInfo;
978 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
983 ModestMailOperationPrivate *priv = NULL;
984 SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
986 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
989 g_warning ("%s: priv->error != NULL", __FUNCTION__);
990 g_error_free(priv->error);
993 priv->error = (err == NULL) ? NULL : g_error_copy(err);
995 if ((!priv->error) && (info->draft_msg != NULL)) {
996 TnyHeader *header = tny_msg_get_header (info->draft_msg);
997 TnyFolder *src_folder = tny_header_get_folder (header);
999 /* Remove the old draft */
1000 tny_folder_remove_msg (src_folder, header, NULL);
1002 /* Synchronize to expunge and to update the msg counts */
1003 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
1004 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
1006 g_object_unref (G_OBJECT(header));
1007 g_object_unref (G_OBJECT(src_folder));
1011 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1013 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1015 /* Call the user callback */
1017 info->callback (info->mailop, info->msg, info->user_data);
1019 if (info->transport_account)
1020 g_object_unref (G_OBJECT(info->transport_account));
1021 if (info->draft_msg)
1022 g_object_unref (G_OBJECT (info->draft_msg));
1024 g_object_unref (G_OBJECT(info->drafts));
1026 g_object_unref (G_OBJECT (info->msg));
1027 g_slice_free (SaveToDraftsAddMsgInfo, info);
1029 modest_mail_operation_notify_end (info->mailop);
1030 g_object_unref(info->mailop);
1035 TnyTransportAccount *transport_account;
1037 SaveToDraftstCallback callback;
1042 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1046 TnyFolder *drafts = NULL;
1047 ModestMailOperationPrivate *priv = NULL;
1048 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1050 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1053 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1054 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1055 "modest: failed to create a new msg\n");
1057 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1058 TNY_FOLDER_TYPE_DRAFTS);
1060 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1061 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1062 "modest: failed to create a new msg\n");
1067 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1068 cb_info->transport_account = g_object_ref(info->transport_account);
1069 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1070 cb_info->callback = info->callback;
1071 cb_info->user_data = info->user_data;
1072 cb_info->drafts = g_object_ref(drafts);
1073 cb_info->msg = g_object_ref(msg);
1074 cb_info->mailop = g_object_ref(self);
1075 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1078 /* Call the user callback */
1079 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1081 info->callback (self, msg, info->user_data);
1082 modest_mail_operation_notify_end (self);
1086 g_object_unref (G_OBJECT(drafts));
1087 if (info->draft_msg)
1088 g_object_unref (G_OBJECT (info->draft_msg));
1089 if (info->transport_account)
1090 g_object_unref (G_OBJECT(info->transport_account));
1091 g_slice_free (SaveToDraftsInfo, info);
1095 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1096 TnyTransportAccount *transport_account,
1098 const gchar *from, const gchar *to,
1099 const gchar *cc, const gchar *bcc,
1100 const gchar *subject, const gchar *plain_body,
1101 const gchar *html_body,
1102 const GList *attachments_list,
1103 const GList *images_list,
1104 TnyHeaderFlags priority_flags,
1105 SaveToDraftstCallback callback,
1108 ModestMailOperationPrivate *priv = NULL;
1109 SaveToDraftsInfo *info = NULL;
1111 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1112 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1114 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1116 /* Get account and set it into mail_operation */
1117 priv->account = g_object_ref (transport_account);
1118 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1120 info = g_slice_new0 (SaveToDraftsInfo);
1121 info->transport_account = g_object_ref (transport_account);
1122 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1123 info->callback = callback;
1124 info->user_data = user_data;
1126 modest_mail_operation_notify_start (self);
1127 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1128 attachments_list, images_list, priority_flags,
1129 modest_mail_operation_save_to_drafts_cb, info);
1134 ModestMailOperation *mail_op;
1135 TnyMimePart *mime_part;
1137 GetMimePartSizeCallback callback;
1139 } GetMimePartSizeInfo;
1141 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
1142 /* We use this folder observer to track the headers that have been
1143 * added to a folder */
1146 TnyList *new_headers;
1147 } InternalFolderObserver;
1150 GObjectClass parent;
1151 } InternalFolderObserverClass;
1153 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1155 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1156 internal_folder_observer,
1158 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1162 foreach_add_item (gpointer header, gpointer user_data)
1164 tny_list_prepend (TNY_LIST (user_data),
1165 g_object_ref (G_OBJECT (header)));
1168 /* This is the method that looks for new messages in a folder */
1170 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1172 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1174 TnyFolderChangeChanged changed;
1176 changed = tny_folder_change_get_changed (change);
1178 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1181 /* Get added headers */
1182 list = tny_simple_list_new ();
1183 tny_folder_change_get_added_headers (change, list);
1185 /* Add them to the folder observer */
1186 tny_list_foreach (list, foreach_add_item,
1187 derived->new_headers);
1189 g_object_unref (G_OBJECT (list));
1194 internal_folder_observer_init (InternalFolderObserver *self)
1196 self->new_headers = tny_simple_list_new ();
1199 internal_folder_observer_finalize (GObject *object)
1201 InternalFolderObserver *self;
1203 self = (InternalFolderObserver *) object;
1204 g_object_unref (self->new_headers);
1206 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1209 tny_folder_observer_init (TnyFolderObserverIface *iface)
1211 iface->update_func = internal_folder_observer_update;
1214 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1216 GObjectClass *object_class;
1218 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1219 object_class = (GObjectClass*) klass;
1220 object_class->finalize = internal_folder_observer_finalize;
1225 ModestMailOperation *mail_op;
1226 gchar *account_name;
1227 UpdateAccountCallback callback;
1232 TnyFolderObserver *inbox_observer;
1233 guint update_timeout;
1234 } UpdateAccountInfo;
1238 destroy_update_account_info (UpdateAccountInfo *info)
1240 if (info->update_timeout) {
1241 g_source_remove (info->update_timeout);
1242 info->update_timeout = 0;
1245 g_free (info->account_name);
1246 g_object_unref (info->folders);
1247 g_object_unref (info->mail_op);
1248 g_slice_free (UpdateAccountInfo, info);
1252 update_account_get_msg_async_cb (TnyFolder *folder,
1258 GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1260 /* Just delete the helper. Don't do anything with the new
1261 msg. There is also no need to check for errors */
1262 g_object_unref (msg_info->mail_op);
1263 g_object_unref (msg_info->header);
1264 g_slice_free (GetMsgInfo, msg_info);
1269 inbox_refreshed_cb (TnyFolder *inbox,
1274 UpdateAccountInfo *info;
1275 ModestMailOperationPrivate *priv;
1276 TnyIterator *new_headers_iter;
1277 GPtrArray *new_headers_array = NULL;
1278 gint max_size, retrieve_limit, i;
1279 ModestAccountMgr *mgr;
1280 ModestAccountRetrieveType retrieve_type;
1281 TnyList *new_headers = NULL;
1282 gboolean headers_only;
1283 TnyTransportAccount *transport_account;
1284 ModestTnySendQueue *send_queue;
1286 info = (UpdateAccountInfo *) user_data;
1287 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1288 mgr = modest_runtime_get_account_mgr ();
1290 if (canceled || err || !inbox) {
1291 /* Try to send anyway */
1295 /* Get the message max size */
1296 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1297 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1299 max_size = G_MAXINT;
1301 max_size = max_size * KB;
1303 /* Create the new headers array. We need it to sort the
1304 new headers by date */
1305 new_headers_array = g_ptr_array_new ();
1306 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1307 while (!tny_iterator_is_done (new_headers_iter)) {
1308 TnyHeader *header = NULL;
1310 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1311 /* Apply per-message size limits */
1312 if (tny_header_get_message_size (header) < max_size)
1313 g_ptr_array_add (new_headers_array, g_object_ref (header));
1315 g_object_unref (header);
1316 tny_iterator_next (new_headers_iter);
1318 g_object_unref (new_headers_iter);
1319 tny_folder_remove_observer (inbox, info->inbox_observer);
1320 g_object_unref (info->inbox_observer);
1321 info->inbox_observer = NULL;
1323 /* Update the last updated key, even if we don't have to get new headers */
1324 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1326 if (new_headers_array->len == 0)
1329 /* Get per-account message amount retrieval limit */
1330 retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1331 if (retrieve_limit == 0)
1332 retrieve_limit = G_MAXINT;
1334 /* Get per-account retrieval type */
1335 retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1336 headers_only = (retrieve_type == MODEST_ACCOUNT_RETRIEVE_HEADERS_ONLY);
1339 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1341 /* TODO: Ask the user, instead of just failing,
1342 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1343 * all' and 'Newest only' buttons. */
1344 if (new_headers_array->len > retrieve_limit) {
1348 if (!headers_only) {
1350 const gint msg_list_size = compute_message_array_size (new_headers_array);
1353 priv->total = MIN (new_headers_array->len, retrieve_limit);
1354 while (msg_num < priv->total) {
1355 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1356 TnyFolder *folder = tny_header_get_folder (header);
1357 GetMsgInfo *msg_info;
1359 /* Create the message info */
1360 msg_info = g_slice_new0 (GetMsgInfo);
1361 msg_info->mail_op = g_object_ref (info->mail_op);
1362 msg_info->header = g_object_ref (header);
1363 msg_info->total_bytes = msg_list_size;
1365 /* Get message in an async way */
1366 tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1367 get_msg_status_cb, msg_info);
1369 g_object_unref (folder);
1375 /* Copy the headers to a list and free the array */
1376 new_headers = tny_simple_list_new ();
1377 for (i=0; i < new_headers_array->len; i++) {
1378 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1379 tny_list_append (new_headers, G_OBJECT (header));
1381 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1382 g_ptr_array_free (new_headers_array, FALSE);
1389 /* Get the transport account */
1390 transport_account = (TnyTransportAccount *)
1391 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1392 info->account_name);
1395 send_queue = modest_runtime_get_send_queue (transport_account);
1396 modest_tny_send_queue_try_to_send (send_queue);
1398 /* Check if the operation was a success */
1400 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1402 /* Set the account back to not busy */
1403 modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1405 /* Call the user callback */
1407 info->callback (info->mail_op, new_headers, info->user_data);
1409 /* Notify about operation end */
1410 modest_mail_operation_notify_end (info->mail_op);
1414 g_object_unref (new_headers);
1415 destroy_update_account_info (info);
1419 recurse_folders_async_cb (TnyFolderStore *folder_store,
1425 UpdateAccountInfo *info;
1426 ModestMailOperationPrivate *priv;
1428 info = (UpdateAccountInfo *) user_data;
1429 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1431 if (err || canceled) {
1432 /* Try to continue anyway */
1434 TnyIterator *iter = tny_list_create_iterator (list);
1435 while (!tny_iterator_is_done (iter)) {
1436 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1437 TnyList *folders = tny_simple_list_new ();
1439 /* Add to the list of all folders */
1440 tny_list_append (info->folders, (GObject *) folder);
1442 /* Add pending call */
1443 info->pending_calls++;
1445 tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb,
1448 g_object_unref (G_OBJECT (folder));
1450 tny_iterator_next (iter);
1452 g_object_unref (G_OBJECT (iter));
1453 g_object_unref (G_OBJECT (list));
1456 /* Remove my own pending call */
1457 info->pending_calls--;
1459 /* This means that we have all the folders */
1460 if (info->pending_calls == 0) {
1461 TnyIterator *iter_all_folders;
1462 TnyFolder *inbox = NULL;
1464 iter_all_folders = tny_list_create_iterator (info->folders);
1466 /* Do a poke status over all folders */
1467 while (!tny_iterator_is_done (iter_all_folders) &&
1468 priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1469 TnyFolder *folder = NULL;
1471 folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1473 if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1474 /* Get a reference to the INBOX */
1475 inbox = g_object_ref (folder);
1477 /* Issue a poke status over the folder */
1479 tny_folder_poke_status (folder);
1482 /* Free and go to next */
1483 g_object_unref (folder);
1484 tny_iterator_next (iter_all_folders);
1486 g_object_unref (iter_all_folders);
1488 /* Stop the progress notification */
1489 g_source_remove (info->update_timeout);
1490 info->update_timeout = 0;
1492 /* Refresh the INBOX */
1494 /* Refresh the folder. Our observer receives
1495 * the new emails during folder refreshes, so
1496 * we can use observer->new_headers
1498 info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1499 tny_folder_add_observer (inbox, info->inbox_observer);
1501 /* Refresh the INBOX */
1502 tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1503 g_object_unref (inbox);
1505 /* We could not perform the inbox refresh but
1506 we'll try to send mails anyway */
1507 inbox_refreshed_cb (inbox, FALSE, NULL, info);
1513 * Issues the "progress-changed" signal. The timer won't be removed,
1514 * so you must call g_source_remove to stop the signal emission
1517 timeout_notify_progress (gpointer data)
1519 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1520 ModestMailOperationState *state;
1522 state = modest_mail_operation_clone_state (mail_op);
1524 /* This is a GDK lock because we are an idle callback and
1525 * the handlers of this signal can contain Gtk+ code */
1527 gdk_threads_enter (); /* CHECKED */
1528 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1529 gdk_threads_leave (); /* CHECKED */
1531 g_slice_free (ModestMailOperationState, state);
1537 modest_mail_operation_update_account (ModestMailOperation *self,
1538 const gchar *account_name,
1540 UpdateAccountCallback callback,
1543 UpdateAccountInfo *info = NULL;
1544 ModestMailOperationPrivate *priv = NULL;
1545 ModestTnyAccountStore *account_store = NULL;
1546 TnyStoreAccount *store_account = NULL;
1549 /* Init mail operation */
1550 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1553 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1554 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1556 /* Get the store account */
1557 account_store = modest_runtime_get_account_store ();
1558 store_account = (TnyStoreAccount *)
1559 modest_tny_account_store_get_server_account (account_store,
1561 TNY_ACCOUNT_TYPE_STORE);
1562 priv->account = g_object_ref (store_account);
1564 /* Create the helper object */
1565 info = g_slice_new0 (UpdateAccountInfo);
1566 info->pending_calls = 1;
1567 info->folders = tny_simple_list_new ();
1568 info->mail_op = g_object_ref (self);
1569 info->poke_all = poke_all;
1570 info->account_name = g_strdup (account_name);
1571 info->callback = callback;
1572 info->user_data = user_data;
1573 info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1575 /* Set account busy */
1576 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1577 modest_mail_operation_notify_start (self);
1579 /* Get all folders and continue in the callback */
1580 folders = tny_simple_list_new ();
1581 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1582 folders, recurse_folders_async_cb,
1587 * Used to notify the queue from the main
1588 * loop. We call it inside an idle call to achieve that
1591 idle_notify_queue (gpointer data)
1593 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1595 gdk_threads_enter ();
1596 modest_mail_operation_notify_end (mail_op);
1597 gdk_threads_leave ();
1598 g_object_unref (mail_op);
1604 compare_headers_by_date (gconstpointer a,
1607 TnyHeader **header1, **header2;
1608 time_t sent1, sent2;
1610 header1 = (TnyHeader **) a;
1611 header2 = (TnyHeader **) b;
1613 sent1 = tny_header_get_date_sent (*header1);
1614 sent2 = tny_header_get_date_sent (*header2);
1616 /* We want the most recent ones (greater time_t) at the
1625 /* ******************************************************************* */
1626 /* ************************** STORE ACTIONS ************************* */
1627 /* ******************************************************************* */
1631 modest_mail_operation_create_folder (ModestMailOperation *self,
1632 TnyFolderStore *parent,
1635 ModestMailOperationPrivate *priv;
1636 TnyFolder *new_folder = NULL;
1638 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1639 g_return_val_if_fail (name, NULL);
1641 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1642 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1643 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1644 g_object_ref (parent) :
1645 modest_tny_folder_get_account (TNY_FOLDER (parent));
1647 /* Check for already existing folder */
1648 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1649 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1650 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1651 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1652 _CS("ckdg_ib_folder_already_exists"));
1656 if (TNY_IS_FOLDER (parent)) {
1657 /* Check folder rules */
1658 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1659 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1660 /* Set status failed and set an error */
1661 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1662 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1663 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1664 _("mail_in_ui_folder_create_error"));
1668 if (!strcmp (name, " ") || strchr (name, '/')) {
1669 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1670 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1671 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1672 _("mail_in_ui_folder_create_error"));
1676 /* Create the folder */
1677 modest_mail_operation_notify_start (self);
1678 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1679 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1681 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1684 /* Notify about operation end */
1685 modest_mail_operation_notify_end (self);
1691 modest_mail_operation_remove_folder (ModestMailOperation *self,
1693 gboolean remove_to_trash)
1695 TnyAccount *account;
1696 ModestMailOperationPrivate *priv;
1697 ModestTnyFolderRules rules;
1699 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1700 g_return_if_fail (TNY_IS_FOLDER (folder));
1702 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1704 /* Check folder rules */
1705 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1706 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1707 /* Set status failed and set an error */
1708 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1709 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1710 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1711 _("mail_in_ui_folder_delete_error"));
1715 /* Get the account */
1716 account = modest_tny_folder_get_account (folder);
1717 priv->account = g_object_ref(account);
1718 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1720 /* Delete folder or move to trash */
1721 if (remove_to_trash) {
1722 TnyFolder *trash_folder = NULL;
1723 trash_folder = modest_tny_account_get_special_folder (account,
1724 TNY_FOLDER_TYPE_TRASH);
1725 /* TODO: error_handling */
1727 modest_mail_operation_notify_start (self);
1728 modest_mail_operation_xfer_folder (self, folder,
1729 TNY_FOLDER_STORE (trash_folder),
1731 g_object_unref (trash_folder);
1734 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1736 modest_mail_operation_notify_start (self);
1737 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1738 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1741 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1743 g_object_unref (parent);
1745 g_warning ("%s: could not get parent folder", __FUNCTION__);
1747 g_object_unref (G_OBJECT (account));
1750 /* Notify about operation end */
1751 modest_mail_operation_notify_end (self);
1755 transfer_folder_status_cb (GObject *obj,
1759 ModestMailOperation *self;
1760 ModestMailOperationPrivate *priv;
1761 ModestMailOperationState *state;
1762 XFerMsgAsyncHelper *helper;
1764 g_return_if_fail (status != NULL);
1765 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1767 helper = (XFerMsgAsyncHelper *) user_data;
1768 g_return_if_fail (helper != NULL);
1770 self = helper->mail_op;
1771 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1773 priv->done = status->position;
1774 priv->total = status->of_total;
1776 state = modest_mail_operation_clone_state (self);
1778 /* This is not a GDK lock because we are a Tinymail callback
1779 * which is already GDK locked by Tinymail */
1781 /* no gdk_threads_enter (), CHECKED */
1783 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1785 /* no gdk_threads_leave (), CHECKED */
1787 g_slice_free (ModestMailOperationState, state);
1792 transfer_folder_cb (TnyFolder *folder,
1794 TnyFolderStore *into,
1795 TnyFolder *new_folder,
1799 XFerMsgAsyncHelper *helper;
1800 ModestMailOperation *self = NULL;
1801 ModestMailOperationPrivate *priv = NULL;
1803 helper = (XFerMsgAsyncHelper *) user_data;
1804 g_return_if_fail (helper != NULL);
1806 self = helper->mail_op;
1807 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1810 priv->error = g_error_copy (err);
1812 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1813 } else if (cancelled) {
1814 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1815 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1816 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1817 _("Transference of %s was cancelled."),
1818 tny_folder_get_name (folder));
1821 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1824 /* Notify about operation end */
1825 modest_mail_operation_notify_end (self);
1827 /* If user defined callback function was defined, call it */
1828 if (helper->user_callback) {
1830 /* This is not a GDK lock because we are a Tinymail callback
1831 * which is already GDK locked by Tinymail */
1833 /* no gdk_threads_enter (), CHECKED */
1834 helper->user_callback (self, helper->user_data);
1835 /* no gdk_threads_leave () , CHECKED */
1839 g_object_unref (helper->mail_op);
1840 g_slice_free (XFerMsgAsyncHelper, helper);
1845 * This function checks if the new name is a valid name for our local
1846 * folders account. The new name could not be the same than then name
1847 * of any of the mandatory local folders
1849 * We can not rely on tinymail because tinymail does not check the
1850 * name of the virtual folders that the account could have in the case
1851 * that we're doing a rename (because it directly calls Camel which
1852 * knows nothing about our virtual folders).
1854 * In the case of an actual copy/move (i.e. move/copy a folder between
1855 * accounts) tinymail uses the tny_folder_store_create_account which
1856 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1857 * checks the new name of the folder, so this call in that case
1858 * wouldn't be needed. *But* NOTE that if tinymail changes its
1859 * implementation (if folder transfers within the same account is no
1860 * longer implemented as a rename) this call will allow Modest to work
1863 * If the new name is not valid, this function will set the status to
1864 * failed and will set also an error in the mail operation
1867 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1868 TnyFolderStore *into,
1869 const gchar *new_name)
1871 if (TNY_IS_ACCOUNT (into) &&
1872 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1873 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1875 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1876 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1877 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1878 _CS("ckdg_ib_folder_already_exists"));
1885 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1887 TnyFolderStore *parent,
1888 gboolean delete_original,
1889 XferAsyncUserCallback user_callback,
1892 ModestMailOperationPrivate *priv = NULL;
1893 ModestTnyFolderRules parent_rules = 0, rules;
1894 XFerMsgAsyncHelper *helper = NULL;
1895 const gchar *folder_name = NULL;
1896 const gchar *error_msg;
1898 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1899 g_return_if_fail (TNY_IS_FOLDER (folder));
1900 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1902 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1903 folder_name = tny_folder_get_name (folder);
1905 /* Set the error msg */
1906 error_msg = _("mail_in_ui_folder_move_target_error");
1908 /* Get account and set it into mail_operation */
1909 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1910 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1911 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1913 /* Get folder rules */
1914 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1915 if (TNY_IS_FOLDER (parent))
1916 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1918 /* Apply operation constraints */
1919 if ((gpointer) parent == (gpointer) folder ||
1920 (!TNY_IS_FOLDER_STORE (parent)) ||
1921 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1924 } else if (TNY_IS_FOLDER (parent) &&
1925 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1929 } else if (TNY_IS_FOLDER (parent) &&
1930 TNY_IS_FOLDER_STORE (folder) &&
1931 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1932 TNY_FOLDER_STORE (folder))) {
1933 /* Do not move a parent into a child */
1935 } else if (TNY_IS_FOLDER_STORE (parent) &&
1936 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1937 /* Check that the new folder name is not used by any
1940 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1941 /* Check that the new folder name is not used by any
1942 special local folder */
1945 /* Create the helper */
1946 helper = g_slice_new0 (XFerMsgAsyncHelper);
1947 helper->mail_op = g_object_ref (self);
1948 helper->dest_folder = NULL;
1949 helper->headers = NULL;
1950 helper->user_callback = user_callback;
1951 helper->user_data = user_data;
1953 /* Move/Copy folder */
1954 modest_mail_operation_notify_start (self);
1955 tny_folder_copy_async (folder,
1957 tny_folder_get_name (folder),
1960 transfer_folder_status_cb,
1966 /* Set status failed and set an error */
1967 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1968 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1969 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1972 /* Call the user callback if exists */
1974 user_callback (self, user_data);
1976 /* Notify the queue */
1977 modest_mail_operation_notify_end (self);
1981 modest_mail_operation_rename_folder (ModestMailOperation *self,
1985 ModestMailOperationPrivate *priv;
1986 ModestTnyFolderRules rules;
1987 XFerMsgAsyncHelper *helper;
1989 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1990 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1991 g_return_if_fail (name);
1993 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1995 /* Get account and set it into mail_operation */
1996 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1997 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1999 /* Check folder rules */
2000 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2001 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
2002 /* Set status failed and set an error */
2003 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2004 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2005 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2006 _("FIXME: unable to rename"));
2008 /* Notify about operation end */
2009 modest_mail_operation_notify_end (self);
2010 } else if (!strcmp (name, " ") || strchr (name, '/')) {
2011 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2012 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2013 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2014 _("FIXME: unable to rename"));
2015 /* Notify about operation end */
2016 modest_mail_operation_notify_end (self);
2018 TnyFolderStore *into;
2020 into = tny_folder_get_folder_store (folder);
2022 /* Check that the new folder name is not used by any
2023 special local folder */
2024 if (new_name_valid_if_local_account (priv, into, name)) {
2025 /* Create the helper */
2026 helper = g_slice_new0 (XFerMsgAsyncHelper);
2027 helper->mail_op = g_object_ref(self);
2028 helper->dest_folder = NULL;
2029 helper->headers = NULL;
2030 helper->user_callback = NULL;
2031 helper->user_data = NULL;
2033 /* Rename. Camel handles folder subscription/unsubscription */
2034 modest_mail_operation_notify_start (self);
2035 tny_folder_copy_async (folder, into, name, TRUE,
2037 transfer_folder_status_cb,
2040 modest_mail_operation_notify_end (self);
2042 g_object_unref (into);
2046 /* ******************************************************************* */
2047 /* ************************** MSG ACTIONS ************************* */
2048 /* ******************************************************************* */
2051 modest_mail_operation_get_msg (ModestMailOperation *self,
2053 GetMsgAsyncUserCallback user_callback,
2056 GetMsgInfo *helper = NULL;
2058 ModestMailOperationPrivate *priv;
2060 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2061 g_return_if_fail (TNY_IS_HEADER (header));
2063 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2064 folder = tny_header_get_folder (header);
2066 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2070 /* Get account and set it into mail_operation */
2071 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2073 /* Check for cached messages */
2074 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2075 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2077 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2079 /* Create the helper */
2080 helper = g_slice_new0 (GetMsgInfo);
2081 helper->header = g_object_ref (header);
2082 helper->mail_op = g_object_ref (self);
2083 helper->user_callback = user_callback;
2084 helper->user_data = user_data;
2085 helper->destroy_notify = NULL;
2086 helper->last_total_bytes = 0;
2087 helper->sum_total_bytes = 0;
2088 helper->total_bytes = tny_header_get_message_size (header);
2090 modest_mail_operation_notify_start (self);
2091 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2093 g_object_unref (G_OBJECT (folder));
2097 get_msg_status_cb (GObject *obj,
2101 GetMsgInfo *helper = NULL;
2103 g_return_if_fail (status != NULL);
2104 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2106 helper = (GetMsgInfo *) user_data;
2107 g_return_if_fail (helper != NULL);
2109 /* Notify progress */
2110 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2111 &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2115 get_msg_async_cb (TnyFolder *folder,
2121 GetMsgInfo *info = NULL;
2122 ModestMailOperationPrivate *priv = NULL;
2125 info = (GetMsgInfo *) user_data;
2127 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2129 finished = (priv->done == priv->total) ? TRUE : FALSE;
2132 if (canceled || err) {
2133 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2135 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2136 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2139 /* Set the success status before calling the user callback */
2140 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2141 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2145 /* Call the user callback */
2146 if (info->user_callback)
2147 info->user_callback (info->mail_op, info->header, canceled,
2148 msg, err, info->user_data);
2150 /* Notify about operation end if this is the last callback */
2152 /* Free user data */
2153 if (info->destroy_notify)
2154 info->destroy_notify (info->user_data);
2156 /* Notify about operation end */
2157 modest_mail_operation_notify_end (info->mail_op);
2161 g_object_unref (info->header);
2162 g_object_unref (info->mail_op);
2163 g_slice_free (GetMsgInfo, info);
2167 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2168 TnyList *header_list,
2169 GetMsgAsyncUserCallback user_callback,
2171 GDestroyNotify notify)
2173 ModestMailOperationPrivate *priv = NULL;
2174 gboolean size_ok = TRUE;
2176 TnyIterator *iter = NULL;
2178 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2180 /* Init mail operation */
2181 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2182 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2183 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2185 priv->total = tny_list_get_length(header_list);
2187 /* Get account and set it into mail_operation */
2188 if (tny_list_get_length (header_list) >= 1) {
2189 iter = tny_list_create_iterator (header_list);
2190 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2192 TnyFolder *folder = tny_header_get_folder (header);
2194 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2195 g_object_unref (folder);
2197 g_object_unref (header);
2200 if (tny_list_get_length (header_list) == 1) {
2201 g_object_unref (iter);
2206 /* Get msg size limit */
2207 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2208 MODEST_CONF_MSG_SIZE_LIMIT,
2211 g_clear_error (&(priv->error));
2212 max_size = G_MAXINT;
2214 max_size = max_size * KB;
2217 /* Check message size limits. If there is only one message
2218 always retrieve it */
2220 while (!tny_iterator_is_done (iter) && size_ok) {
2221 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2223 if (tny_header_get_message_size (header) >= max_size)
2225 g_object_unref (header);
2228 tny_iterator_next (iter);
2230 g_object_unref (iter);
2234 const gint msg_list_size = compute_message_list_size (header_list);
2236 modest_mail_operation_notify_start (self);
2237 iter = tny_list_create_iterator (header_list);
2238 while (!tny_iterator_is_done (iter)) {
2239 GetMsgInfo *msg_info = NULL;
2240 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2241 TnyFolder *folder = tny_header_get_folder (header);
2243 /* Create the message info */
2244 msg_info = g_slice_new0 (GetMsgInfo);
2245 msg_info->mail_op = g_object_ref (self);
2246 msg_info->header = g_object_ref (header);
2247 msg_info->user_callback = user_callback;
2248 msg_info->user_data = user_data;
2249 msg_info->destroy_notify = notify;
2250 msg_info->last_total_bytes = 0;
2251 msg_info->sum_total_bytes = 0;
2252 msg_info->total_bytes = msg_list_size;
2254 /* The callback will call it per each header */
2255 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2257 /* Free and go on */
2258 g_object_unref (header);
2259 g_object_unref (folder);
2260 tny_iterator_next (iter);
2262 g_object_unref (iter);
2264 /* Set status failed and set an error */
2265 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2266 /* FIXME: the error msg is different for pop */
2267 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2268 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2269 _("emev_ni_ui_imap_msg_size_exceed_error"));
2270 /* Remove from queue and free resources */
2271 modest_mail_operation_notify_end (self);
2279 modest_mail_operation_remove_msg (ModestMailOperation *self,
2281 gboolean remove_to_trash /*ignored*/)
2284 ModestMailOperationPrivate *priv;
2286 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2287 g_return_if_fail (TNY_IS_HEADER (header));
2289 if (remove_to_trash)
2290 g_warning ("remove to trash is not implemented");
2292 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2293 folder = tny_header_get_folder (header);
2295 /* Get account and set it into mail_operation */
2296 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2297 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2298 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2300 /* remove message from folder */
2301 tny_folder_remove_msg (folder, header, &(priv->error));
2303 gboolean expunge, leave_on_server;
2304 const gchar *account_name;
2305 TnyAccount *account;
2307 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2308 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2310 modest_mail_operation_notify_start (self);
2312 /* Get leave on server setting */
2313 account = tny_folder_get_account (folder);
2314 account_name = modest_tny_account_get_parent_modest_account_name_for_server_account (account);
2316 modest_account_mgr_get_leave_on_server (modest_runtime_get_account_mgr (),
2319 if (TNY_IS_CAMEL_POP_FOLDER (folder) && !leave_on_server)
2325 tny_folder_sync_async(folder, expunge, NULL, NULL, NULL);
2328 g_object_unref (account);
2334 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2336 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2339 g_object_unref (G_OBJECT (folder));
2341 /* Notify about operation end */
2342 modest_mail_operation_notify_end (self);
2346 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2348 gboolean remove_to_trash /*ignored*/)
2351 ModestMailOperationPrivate *priv;
2352 TnyIterator *iter = NULL;
2353 TnyHeader *header = NULL;
2354 TnyList *remove_headers = NULL;
2355 TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2357 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2358 g_return_if_fail (TNY_IS_LIST (headers));
2360 if (remove_to_trash)
2361 g_warning ("remove to trash is not implemented");
2363 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2365 remove_headers = g_object_ref(headers);
2367 /* Get folder from first header and sync it */
2368 iter = tny_list_create_iterator (headers);
2369 header = TNY_HEADER (tny_iterator_get_current (iter));
2370 folder = tny_header_get_folder (header);
2372 /* Don't remove messages that are being sent */
2373 if (modest_tny_folder_is_local_folder (folder)) {
2374 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2376 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2377 TnyTransportAccount *traccount = NULL;
2378 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2379 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2381 ModestTnySendQueueStatus status;
2382 ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2383 TnyIterator *iter = tny_list_create_iterator(headers);
2384 g_object_unref(remove_headers);
2385 remove_headers = TNY_LIST(tny_simple_list_new());
2386 while (!tny_iterator_is_done(iter)) {
2388 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2389 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2390 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2391 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2392 tny_list_append(remove_headers, G_OBJECT(hdr));
2394 g_object_unref(hdr);
2396 tny_iterator_next(iter);
2398 g_object_unref(iter);
2399 g_object_unref(traccount);
2403 /* Get account and set it into mail_operation */
2404 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2405 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2406 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2408 /* remove message from folder */
2409 modest_mail_operation_notify_start (self);
2411 tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2413 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2414 TNY_IS_CAMEL_POP_FOLDER (folder))
2415 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2418 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2424 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2426 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2429 g_object_unref (remove_headers);
2430 g_object_unref (header);
2431 g_object_unref (iter);
2432 g_object_unref (G_OBJECT (folder));
2434 /* Notify about operation end */
2435 modest_mail_operation_notify_end (self);
2439 notify_progress_of_multiple_messages (ModestMailOperation *self,
2441 gint *last_total_bytes,
2442 gint *sum_total_bytes,
2444 gboolean increment_done)
2446 ModestMailOperationPrivate *priv;
2447 ModestMailOperationState *state;
2448 gboolean is_num_bytes;
2450 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2452 /* We know that tinymail sends us information about
2453 transferred bytes with this particular message */
2454 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2456 state = modest_mail_operation_clone_state (self);
2457 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2458 /* We know that we're in a different message when the
2459 total number of bytes to transfer is different. Of
2460 course it could fail if we're transferring messages
2461 of the same size, but this is a workarround */
2462 if (status->of_total != *last_total_bytes) {
2463 /* We need to increment the done when there is
2464 no information about each individual
2465 message, we need to do this in message
2466 transfers, and we don't do it for getting
2470 *sum_total_bytes += *last_total_bytes;
2471 *last_total_bytes = status->of_total;
2473 state->bytes_done += status->position + *sum_total_bytes;
2474 state->bytes_total = total_bytes;
2476 /* Notify the status change. Only notify about changes
2477 referred to bytes */
2478 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2482 g_slice_free (ModestMailOperationState, state);
2486 transfer_msgs_status_cb (GObject *obj,
2490 XFerMsgAsyncHelper *helper;
2492 g_return_if_fail (status != NULL);
2493 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2495 helper = (XFerMsgAsyncHelper *) user_data;
2496 g_return_if_fail (helper != NULL);
2498 /* Notify progress */
2499 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2500 &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2505 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2507 XFerMsgAsyncHelper *helper;
2508 ModestMailOperation *self;
2509 ModestMailOperationPrivate *priv;
2510 TnyIterator *iter = NULL;
2511 TnyHeader *header = NULL;
2513 helper = (XFerMsgAsyncHelper *) user_data;
2514 self = helper->mail_op;
2516 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2519 priv->error = g_error_copy (err);
2521 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2522 } else if (cancelled) {
2523 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2524 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2525 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2526 _("Error trying to refresh the contents of %s"),
2527 tny_folder_get_name (folder));
2530 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2532 /* Update folder counts */
2533 tny_folder_poke_status (folder);
2534 tny_folder_poke_status (helper->dest_folder);
2538 /* Mark headers as deleted and seen */
2539 if ((helper->delete) &&
2540 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2541 iter = tny_list_create_iterator (helper->headers);
2542 while (!tny_iterator_is_done (iter)) {
2543 header = TNY_HEADER (tny_iterator_get_current (iter));
2544 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2545 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2546 g_object_unref (header);
2548 tny_iterator_next (iter);
2554 /* Notify about operation end */
2555 modest_mail_operation_notify_end (self);
2557 /* If user defined callback function was defined, call it */
2558 if (helper->user_callback) {
2559 /* This is not a GDK lock because we are a Tinymail callback and
2560 * Tinymail already acquires the Gdk lock */
2562 /* no gdk_threads_enter (), CHECKED */
2563 helper->user_callback (self, helper->user_data);
2564 /* no gdk_threads_leave (), CHECKED */
2568 if (helper->headers)
2569 g_object_unref (helper->headers);
2570 if (helper->dest_folder)
2571 g_object_unref (helper->dest_folder);
2572 if (helper->mail_op)
2573 g_object_unref (helper->mail_op);
2575 g_object_unref (folder);
2577 g_object_unref (iter);
2578 g_slice_free (XFerMsgAsyncHelper, helper);
2582 compute_message_list_size (TnyList *headers)
2587 iter = tny_list_create_iterator (headers);
2588 while (!tny_iterator_is_done (iter)) {
2589 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2590 size += tny_header_get_message_size (header);
2591 g_object_unref (header);
2592 tny_iterator_next (iter);
2594 g_object_unref (iter);
2600 compute_message_array_size (GPtrArray *headers)
2605 for (i = 0; i < headers->len; i++) {
2606 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2607 size += tny_header_get_message_size (header);
2615 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2618 gboolean delete_original,
2619 XferAsyncUserCallback user_callback,
2622 ModestMailOperationPrivate *priv = NULL;
2623 TnyIterator *iter = NULL;
2624 TnyFolder *src_folder = NULL;
2625 XFerMsgAsyncHelper *helper = NULL;
2626 TnyHeader *header = NULL;
2627 ModestTnyFolderRules rules = 0;
2629 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2630 g_return_if_fail (headers && TNY_IS_LIST (headers));
2631 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2633 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2634 priv->total = tny_list_get_length (headers);
2636 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2637 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2639 /* Apply folder rules */
2640 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2641 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2642 /* Set status failed and set an error */
2643 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2644 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2645 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2646 _CS("ckct_ib_unable_to_paste_here"));
2647 /* Notify the queue */
2648 modest_mail_operation_notify_end (self);
2652 /* Get source folder */
2653 iter = tny_list_create_iterator (headers);
2654 header = TNY_HEADER (tny_iterator_get_current (iter));
2656 src_folder = tny_header_get_folder (header);
2657 g_object_unref (header);
2659 g_object_unref (iter);
2661 if (src_folder == NULL) {
2662 /* Notify the queue */
2663 modest_mail_operation_notify_end (self);
2665 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2670 /* Check folder source and destination */
2671 if (src_folder == folder) {
2672 /* Set status failed and set an error */
2673 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2674 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2675 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2676 _("mail_in_ui_folder_copy_target_error"));
2678 /* Notify the queue */
2679 modest_mail_operation_notify_end (self);
2682 g_object_unref (src_folder);
2686 /* Create the helper */
2687 helper = g_slice_new0 (XFerMsgAsyncHelper);
2688 helper->mail_op = g_object_ref(self);
2689 helper->dest_folder = g_object_ref(folder);
2690 helper->headers = g_object_ref(headers);
2691 helper->user_callback = user_callback;
2692 helper->user_data = user_data;
2693 helper->delete = delete_original;
2694 helper->last_total_bytes = 0;
2695 helper->sum_total_bytes = 0;
2696 helper->total_bytes = compute_message_list_size (headers);
2698 /* Get account and set it into mail_operation */
2699 priv->account = modest_tny_folder_get_account (src_folder);
2701 /* Transfer messages */
2702 modest_mail_operation_notify_start (self);
2703 tny_folder_transfer_msgs_async (src_folder,
2708 transfer_msgs_status_cb,
2714 on_refresh_folder (TnyFolder *folder,
2719 RefreshAsyncHelper *helper = NULL;
2720 ModestMailOperation *self = NULL;
2721 ModestMailOperationPrivate *priv = NULL;
2723 helper = (RefreshAsyncHelper *) user_data;
2724 self = helper->mail_op;
2725 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2727 g_return_if_fail(priv!=NULL);
2730 priv->error = g_error_copy (error);
2731 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2736 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2737 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2738 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2739 _("Error trying to refresh the contents of %s"),
2740 tny_folder_get_name (folder));
2744 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2747 /* Call user defined callback, if it exists */
2748 if (helper->user_callback) {
2750 /* This is not a GDK lock because we are a Tinymail callback and
2751 * Tinymail already acquires the Gdk lock */
2752 helper->user_callback (self, folder, helper->user_data);
2756 g_slice_free (RefreshAsyncHelper, helper);
2758 /* Notify about operation end */
2759 modest_mail_operation_notify_end (self);
2760 g_object_unref(self);
2764 on_refresh_folder_status_update (GObject *obj,
2768 RefreshAsyncHelper *helper = NULL;
2769 ModestMailOperation *self = NULL;
2770 ModestMailOperationPrivate *priv = NULL;
2771 ModestMailOperationState *state;
2773 g_return_if_fail (user_data != NULL);
2774 g_return_if_fail (status != NULL);
2775 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2777 helper = (RefreshAsyncHelper *) user_data;
2778 self = helper->mail_op;
2779 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2781 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2783 priv->done = status->position;
2784 priv->total = status->of_total;
2786 state = modest_mail_operation_clone_state (self);
2788 /* This is not a GDK lock because we are a Tinymail callback and
2789 * Tinymail already acquires the Gdk lock */
2790 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2792 g_slice_free (ModestMailOperationState, state);
2796 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2798 RefreshAsyncUserCallback user_callback,
2801 ModestMailOperationPrivate *priv = NULL;
2802 RefreshAsyncHelper *helper = NULL;
2804 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2806 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2808 /* Get account and set it into mail_operation */
2809 priv->account = modest_tny_folder_get_account (folder);
2810 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2812 /* Create the helper */
2813 helper = g_slice_new0 (RefreshAsyncHelper);
2814 helper->mail_op = g_object_ref(self);
2815 helper->user_callback = user_callback;
2816 helper->user_data = user_data;
2818 /* Refresh the folder. TODO: tinymail could issue a status
2819 updates before the callback call then this could happen. We
2820 must review the design */
2821 modest_mail_operation_notify_start (self);
2822 tny_folder_refresh_async (folder,
2824 on_refresh_folder_status_update,
2830 modest_mail_operation_notify_start (ModestMailOperation *self)
2832 ModestMailOperationPrivate *priv = NULL;
2834 g_return_if_fail (self);
2836 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2838 /* Ensure that all the fields are filled correctly */
2839 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2841 /* Notify the observers about the mail operation. We do not
2842 wrapp this emission because we assume that this function is
2843 always called from within the main lock */
2844 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2849 * It's used by the mail operation queue to notify the observers
2850 * attached to that signal that the operation finished. We need to use
2851 * that because tinymail does not give us the progress of a given
2852 * operation when it finishes (it directly calls the operation
2856 modest_mail_operation_notify_end (ModestMailOperation *self)
2858 ModestMailOperationPrivate *priv = NULL;
2860 g_return_if_fail (self);
2862 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2864 /* Notify the observers about the mail operation end. We do
2865 not wrapp this emission because we assume that this
2866 function is always called from within the main lock */
2867 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2869 /* Remove the error user data */
2870 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2871 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2875 modest_mail_operation_get_account (ModestMailOperation *self)
2877 ModestMailOperationPrivate *priv = NULL;
2879 g_return_val_if_fail (self, NULL);
2881 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2883 return (priv->account) ? g_object_ref (priv->account) : NULL;
2887 modest_mail_operation_noop (ModestMailOperation *self)
2889 ModestMailOperationPrivate *priv = NULL;
2891 g_return_if_fail (self);
2893 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2894 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2895 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2899 /* This mail operation does nothing actually */
2900 modest_mail_operation_notify_start (self);
2901 modest_mail_operation_notify_end (self);