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 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
393 g_return_if_fail(priv->status != MODEST_MAIL_OPERATION_STATUS_SUCCESS);
395 /* Call the user callback */
396 if (priv->error_checking != NULL)
397 priv->error_checking (self, priv->error_checking_user_data);
401 ModestMailOperationTypeOperation
402 modest_mail_operation_get_type_operation (ModestMailOperation *self)
404 ModestMailOperationPrivate *priv;
406 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
408 return priv->op_type;
412 modest_mail_operation_is_mine (ModestMailOperation *self,
415 ModestMailOperationPrivate *priv;
417 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
418 if (priv->source == NULL) return FALSE;
420 return priv->source == me;
424 modest_mail_operation_get_source (ModestMailOperation *self)
426 ModestMailOperationPrivate *priv;
428 g_return_val_if_fail (self, NULL);
430 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
432 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
436 return (priv->source) ? g_object_ref (priv->source) : NULL;
439 ModestMailOperationStatus
440 modest_mail_operation_get_status (ModestMailOperation *self)
442 ModestMailOperationPrivate *priv;
444 g_return_val_if_fail (self, MODEST_MAIL_OPERATION_STATUS_INVALID);
445 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self),
446 MODEST_MAIL_OPERATION_STATUS_INVALID);
448 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
450 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
451 return MODEST_MAIL_OPERATION_STATUS_INVALID;
458 modest_mail_operation_get_error (ModestMailOperation *self)
460 ModestMailOperationPrivate *priv;
462 g_return_val_if_fail (self, NULL);
463 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), NULL);
465 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
468 g_warning ("BUG: %s: priv == NULL", __FUNCTION__);
476 modest_mail_operation_cancel (ModestMailOperation *self)
478 ModestMailOperationPrivate *priv;
479 gboolean canceled = FALSE;
481 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), FALSE);
483 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
485 /* Note that if we call cancel with an already canceled mail
486 operation the progress changed signal won't be emitted */
487 if (priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED)
491 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
493 /* Cancel the mail operation. We need to wrap it between this
494 start/stop operations to allow following calls to the
496 g_return_val_if_fail (priv->account, FALSE);
498 if (priv->op_type == MODEST_MAIL_OPERATION_TYPE_SEND) {
499 ModestTnySendQueue *queue;
500 queue = modest_runtime_get_send_queue (TNY_TRANSPORT_ACCOUNT (priv->account));
501 /* Cancel sending without removing the item */
502 tny_send_queue_cancel (TNY_SEND_QUEUE (queue), FALSE, NULL);
504 /* Cancel operation */
505 tny_account_cancel (priv->account);
512 modest_mail_operation_get_task_done (ModestMailOperation *self)
514 ModestMailOperationPrivate *priv;
516 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
518 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
523 modest_mail_operation_get_task_total (ModestMailOperation *self)
525 ModestMailOperationPrivate *priv;
527 g_return_val_if_fail (MODEST_IS_MAIL_OPERATION (self), 0);
529 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
534 modest_mail_operation_is_finished (ModestMailOperation *self)
536 ModestMailOperationPrivate *priv;
537 gboolean retval = FALSE;
539 if (!MODEST_IS_MAIL_OPERATION (self)) {
540 g_warning ("%s: invalid parametter", G_GNUC_FUNCTION);
544 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
546 if (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS ||
547 priv->status == MODEST_MAIL_OPERATION_STATUS_FAILED ||
548 priv->status == MODEST_MAIL_OPERATION_STATUS_CANCELED ||
549 priv->status == MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS) {
559 * Creates an image of the current state of a mail operation, the
560 * caller must free it
562 static ModestMailOperationState *
563 modest_mail_operation_clone_state (ModestMailOperation *self)
565 ModestMailOperationState *state;
566 ModestMailOperationPrivate *priv;
568 /* FIXME: this should be fixed properly
570 * in some cases, priv was NULL, so checking here to
573 g_return_val_if_fail (self, NULL);
574 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
575 g_return_val_if_fail (priv, NULL);
580 state = g_slice_new (ModestMailOperationState);
582 state->status = priv->status;
583 state->op_type = priv->op_type;
584 state->done = priv->done;
585 state->total = priv->total;
586 state->finished = modest_mail_operation_is_finished (self);
587 state->bytes_done = 0;
588 state->bytes_total = 0;
593 /* ******************************************************************* */
594 /* ************************** SEND ACTIONS ************************* */
595 /* ******************************************************************* */
598 modest_mail_operation_send_mail (ModestMailOperation *self,
599 TnyTransportAccount *transport_account,
602 TnySendQueue *send_queue = NULL;
603 ModestMailOperationPrivate *priv;
606 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
607 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
608 g_return_if_fail (TNY_IS_MSG (msg));
610 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
612 /* Get account and set it into mail_operation */
613 priv->account = g_object_ref (transport_account);
614 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
618 send_queue = TNY_SEND_QUEUE (modest_runtime_get_send_queue (transport_account));
619 if (!TNY_IS_SEND_QUEUE(send_queue)) {
620 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
621 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
622 "modest: could not find send queue for account\n");
623 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
624 modest_mail_operation_notify_end (self);
627 /* Add the msg to the queue */
628 modest_mail_operation_notify_start (self);
629 modest_tny_send_queue_add (MODEST_TNY_SEND_QUEUE(send_queue),
633 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
635 info = g_slice_new0 (SendMsgInfo);
637 info->mail_op = g_object_ref (self);
638 info->msg = g_object_ref (msg);
639 info->msg_sent_handler = g_signal_connect (G_OBJECT (send_queue), "msg-sent",
640 G_CALLBACK (send_mail_msg_sent_handler), info);
641 info->error_happened_handler = g_signal_connect (G_OBJECT (send_queue), "error-happened",
642 G_CALLBACK (send_mail_error_happened_handler), info);
648 common_send_mail_operation_end (TnySendQueue *queue, TnyMsg *msg,
651 g_signal_handler_disconnect (queue, info->msg_sent_handler);
652 g_signal_handler_disconnect (queue, info->error_happened_handler);
654 g_object_unref (info->msg);
655 modest_mail_operation_notify_end (info->mail_op);
656 g_object_unref (info->mail_op);
658 g_slice_free (SendMsgInfo, info);
662 send_mail_msg_sent_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
663 guint nth, guint total, gpointer userdata)
665 SendMsgInfo *info = (SendMsgInfo *) userdata;
667 if (!strcmp (tny_msg_get_url_string (msg),
668 tny_msg_get_url_string (info->msg))) {
669 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
670 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
672 common_send_mail_operation_end (queue, msg, info);
677 send_mail_error_happened_handler (TnySendQueue *queue, TnyHeader *header, TnyMsg *msg,
678 GError *error, gpointer userdata)
680 SendMsgInfo *info = (SendMsgInfo *) userdata;
682 if (!strcmp (tny_msg_get_url_string (msg),
683 tny_msg_get_url_string (info->msg))) {
684 ModestMailOperationPrivate *priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
685 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
686 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
687 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
688 "modest: send mail failed\n");
690 common_send_mail_operation_end (queue, msg, info);
696 idle_create_msg_cb (gpointer idle_data)
698 CreateMsgIdleInfo *info = (CreateMsgIdleInfo *) idle_data;
700 /* This is a GDK lock because we are an idle callback and
701 * info->callback can contain Gtk+ code */
703 gdk_threads_enter (); /* CHECKED */
704 info->callback (info->mail_op, info->msg, info->userdata);
706 g_object_unref (info->mail_op);
708 g_object_unref (info->msg);
709 g_slice_free (CreateMsgIdleInfo, info);
710 gdk_threads_leave (); /* CHECKED */
716 create_msg_thread (gpointer thread_data)
718 CreateMsgInfo *info = (CreateMsgInfo *) thread_data;
719 TnyMsg *new_msg = NULL;
720 ModestMailOperationPrivate *priv;
722 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mail_op);
723 if (info->html_body == NULL) {
724 new_msg = modest_tny_msg_new (info->to, info->from, info->cc,
725 info->bcc, info->subject, info->plain_body,
726 info->attachments_list);
728 new_msg = modest_tny_msg_new_html_plain (info->to, info->from, info->cc,
729 info->bcc, info->subject, info->html_body,
730 info->plain_body, info->attachments_list,
737 /* Set priority flags in message */
738 header = tny_msg_get_header (new_msg);
739 tny_header_set_flag (header, info->priority_flags);
741 /* Set attachment flags in message */
742 if (info->attachments_list != NULL)
743 tny_header_set_flag (header, TNY_HEADER_FLAG_ATTACHMENTS);
745 g_object_unref (G_OBJECT(header));
747 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
748 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
749 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
750 "modest: failed to create a new msg\n");
758 g_free (info->plain_body);
759 g_free (info->html_body);
760 g_free (info->subject);
761 g_list_foreach (info->attachments_list, (GFunc) g_object_unref, NULL);
762 g_list_free (info->attachments_list);
763 g_list_foreach (info->images_list, (GFunc) g_object_unref, NULL);
764 g_list_free (info->images_list);
766 if (info->callback) {
767 CreateMsgIdleInfo *idle_info;
768 idle_info = g_slice_new0 (CreateMsgIdleInfo);
769 idle_info->mail_op = g_object_ref (info->mail_op);
770 idle_info->msg = (new_msg) ? g_object_ref (new_msg) : NULL;
771 idle_info->callback = info->callback;
772 idle_info->userdata = info->userdata;
773 g_idle_add (idle_create_msg_cb, idle_info);
775 g_idle_add (idle_notify_queue, g_object_ref (info->mail_op));
778 g_object_unref (info->mail_op);
779 g_slice_free (CreateMsgInfo, info);
784 modest_mail_operation_create_msg (ModestMailOperation *self,
785 const gchar *from, const gchar *to,
786 const gchar *cc, const gchar *bcc,
787 const gchar *subject, const gchar *plain_body,
788 const gchar *html_body,
789 const GList *attachments_list,
790 const GList *images_list,
791 TnyHeaderFlags priority_flags,
792 ModestMailOperationCreateMsgCallback callback,
795 CreateMsgInfo *info = NULL;
797 info = g_slice_new0 (CreateMsgInfo);
798 info->mail_op = g_object_ref (self);
800 info->from = g_strdup (from);
801 info->to = g_strdup (to);
802 info->cc = g_strdup (cc);
803 info->bcc = g_strdup (bcc);
804 info->subject = g_strdup (subject);
805 info->plain_body = g_strdup (plain_body);
806 info->html_body = g_strdup (html_body);
807 info->attachments_list = g_list_copy ((GList *) attachments_list);
808 g_list_foreach (info->attachments_list, (GFunc) g_object_ref, NULL);
809 info->images_list = g_list_copy ((GList *) images_list);
810 g_list_foreach (info->images_list, (GFunc) g_object_ref, NULL);
811 info->priority_flags = priority_flags;
813 info->callback = callback;
814 info->userdata = userdata;
816 g_thread_create (create_msg_thread, info, FALSE, NULL);
821 TnyTransportAccount *transport_account;
826 modest_mail_operation_send_new_mail_cb (ModestMailOperation *self,
830 SendNewMailInfo *info = (SendNewMailInfo *) userdata;
831 TnyFolder *draft_folder = NULL;
832 TnyFolder *outbox_folder = NULL;
840 /* Call mail operation */
841 modest_mail_operation_send_mail (self, info->transport_account, msg);
843 /* Remove old mail from its source folder */
844 draft_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
845 TNY_FOLDER_TYPE_DRAFTS);
846 outbox_folder = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
847 TNY_FOLDER_TYPE_OUTBOX);
848 if (info->draft_msg != NULL) {
849 TnyFolder *folder = NULL;
850 TnyFolder *src_folder = NULL;
851 TnyFolderType folder_type;
852 folder = tny_msg_get_folder (info->draft_msg);
853 if (folder == NULL) goto end;
854 folder_type = modest_tny_folder_guess_folder_type (folder);
856 if (folder_type == TNY_FOLDER_TYPE_INVALID)
857 g_warning ("%s: BUG: folder of type TNY_FOLDER_TYPE_INVALID", __FUNCTION__);
859 if (folder_type == TNY_FOLDER_TYPE_OUTBOX)
860 src_folder = outbox_folder;
862 src_folder = draft_folder;
864 /* Note: This can fail (with a warning) if the message is not really already in a folder,
865 * because this function requires it to have a UID. */
866 header = tny_msg_get_header (info->draft_msg);
867 tny_folder_remove_msg (src_folder, header, NULL);
869 tny_folder_sync (folder, TRUE, &err); /* FALSE --> don't expunge */
870 /* tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL); /\* expunge *\/ */
872 g_object_unref (header);
873 g_object_unref (folder);
880 g_object_unref (info->draft_msg);
882 g_object_unref (draft_folder);
884 g_object_unref (outbox_folder);
885 if (info->transport_account)
886 g_object_unref (info->transport_account);
887 g_slice_free (SendNewMailInfo, info);
891 modest_mail_operation_send_new_mail (ModestMailOperation *self,
892 TnyTransportAccount *transport_account,
894 const gchar *from, const gchar *to,
895 const gchar *cc, const gchar *bcc,
896 const gchar *subject, const gchar *plain_body,
897 const gchar *html_body,
898 const GList *attachments_list,
899 const GList *images_list,
900 TnyHeaderFlags priority_flags)
902 ModestMailOperationPrivate *priv = NULL;
903 SendNewMailInfo *info;
905 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
906 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
908 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
909 priv->op_type = MODEST_MAIL_OPERATION_TYPE_SEND;
910 priv->account = TNY_ACCOUNT (g_object_ref (transport_account));
912 /* Check parametters */
914 /* Set status failed and set an error */
915 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
916 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
917 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
918 _("Error trying to send a mail. You need to set at least one recipient"));
921 info = g_slice_new0 (SendNewMailInfo);
922 info->transport_account = transport_account;
923 if (transport_account)
924 g_object_ref (transport_account);
925 info->draft_msg = draft_msg;
927 g_object_ref (draft_msg);
930 modest_mail_operation_notify_start (self);
931 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
932 attachments_list, images_list, priority_flags,
933 modest_mail_operation_send_new_mail_cb, info);
939 TnyTransportAccount *transport_account;
941 SaveToDraftstCallback callback;
945 ModestMailOperation *mailop;
946 } SaveToDraftsAddMsgInfo;
949 modest_mail_operation_save_to_drafts_add_msg_cb(TnyFolder *self,
954 ModestMailOperationPrivate *priv = NULL;
955 SaveToDraftsAddMsgInfo *info = (SaveToDraftsAddMsgInfo *) userdata;
957 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(info->mailop);
960 g_warning ("%s: priv->error != NULL", __FUNCTION__);
961 g_error_free(priv->error);
964 priv->error = (err == NULL) ? NULL : g_error_copy(err);
966 if ((!priv->error) && (info->draft_msg != NULL)) {
967 TnyHeader *header = tny_msg_get_header (info->draft_msg);
968 TnyFolder *src_folder = tny_header_get_folder (header);
970 /* Remove the old draft */
971 tny_folder_remove_msg (src_folder, header, NULL);
973 /* Synchronize to expunge and to update the msg counts */
974 tny_folder_sync_async (info->drafts, TRUE, NULL, NULL, NULL);
975 tny_folder_sync_async (src_folder, TRUE, NULL, NULL, NULL);
977 g_object_unref (G_OBJECT(header));
978 g_object_unref (G_OBJECT(src_folder));
982 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
984 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
986 /* Call the user callback */
988 info->callback (info->mailop, info->msg, info->user_data);
990 if (info->transport_account)
991 g_object_unref (G_OBJECT(info->transport_account));
993 g_object_unref (G_OBJECT (info->draft_msg));
995 g_object_unref (G_OBJECT(info->drafts));
997 g_object_unref (G_OBJECT (info->msg));
998 g_slice_free (SaveToDraftsAddMsgInfo, info);
1000 modest_mail_operation_notify_end (info->mailop);
1001 g_object_unref(info->mailop);
1006 TnyTransportAccount *transport_account;
1008 SaveToDraftstCallback callback;
1013 modest_mail_operation_save_to_drafts_cb (ModestMailOperation *self,
1017 TnyFolder *drafts = NULL;
1018 ModestMailOperationPrivate *priv = NULL;
1019 SaveToDraftsInfo *info = (SaveToDraftsInfo *) userdata;
1021 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1024 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1025 MODEST_MAIL_OPERATION_ERROR_INSTANCE_CREATION_FAILED,
1026 "modest: failed to create a new msg\n");
1028 drafts = modest_tny_account_get_special_folder (TNY_ACCOUNT (info->transport_account),
1029 TNY_FOLDER_TYPE_DRAFTS);
1031 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1032 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
1033 "modest: failed to create a new msg\n");
1038 SaveToDraftsAddMsgInfo *cb_info = g_slice_new(SaveToDraftsAddMsgInfo);
1039 cb_info->transport_account = g_object_ref(info->transport_account);
1040 cb_info->draft_msg = info->draft_msg ? g_object_ref(info->draft_msg) : NULL;
1041 cb_info->callback = info->callback;
1042 cb_info->user_data = info->user_data;
1043 cb_info->drafts = g_object_ref(drafts);
1044 cb_info->msg = g_object_ref(msg);
1045 cb_info->mailop = g_object_ref(self);
1046 tny_folder_add_msg_async(drafts, msg, modest_mail_operation_save_to_drafts_add_msg_cb,
1049 /* Call the user callback */
1050 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1052 info->callback (self, msg, info->user_data);
1053 modest_mail_operation_notify_end (self);
1057 g_object_unref (G_OBJECT(drafts));
1058 if (info->draft_msg)
1059 g_object_unref (G_OBJECT (info->draft_msg));
1060 if (info->transport_account)
1061 g_object_unref (G_OBJECT(info->transport_account));
1062 g_slice_free (SaveToDraftsInfo, info);
1066 modest_mail_operation_save_to_drafts (ModestMailOperation *self,
1067 TnyTransportAccount *transport_account,
1069 const gchar *from, const gchar *to,
1070 const gchar *cc, const gchar *bcc,
1071 const gchar *subject, const gchar *plain_body,
1072 const gchar *html_body,
1073 const GList *attachments_list,
1074 const GList *images_list,
1075 TnyHeaderFlags priority_flags,
1076 SaveToDraftstCallback callback,
1079 ModestMailOperationPrivate *priv = NULL;
1080 SaveToDraftsInfo *info = NULL;
1082 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1083 g_return_if_fail (TNY_IS_TRANSPORT_ACCOUNT (transport_account));
1085 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1087 /* Get account and set it into mail_operation */
1088 priv->account = g_object_ref (transport_account);
1089 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1091 info = g_slice_new0 (SaveToDraftsInfo);
1092 info->transport_account = g_object_ref (transport_account);
1093 info->draft_msg = (draft_msg) ? g_object_ref (draft_msg) : NULL;
1094 info->callback = callback;
1095 info->user_data = user_data;
1097 modest_mail_operation_notify_start (self);
1098 modest_mail_operation_create_msg (self, from, to, cc, bcc, subject, plain_body, html_body,
1099 attachments_list, images_list, priority_flags,
1100 modest_mail_operation_save_to_drafts_cb, info);
1105 ModestMailOperation *mail_op;
1106 TnyMimePart *mime_part;
1108 GetMimePartSizeCallback callback;
1110 } GetMimePartSizeInfo;
1112 /***** I N T E R N A L F O L D E R O B S E R V E R *****/
1113 /* We use this folder observer to track the headers that have been
1114 * added to a folder */
1117 TnyList *new_headers;
1118 } InternalFolderObserver;
1121 GObjectClass parent;
1122 } InternalFolderObserverClass;
1124 static void tny_folder_observer_init (TnyFolderObserverIface *idace);
1126 G_DEFINE_TYPE_WITH_CODE (InternalFolderObserver,
1127 internal_folder_observer,
1129 G_IMPLEMENT_INTERFACE(TNY_TYPE_FOLDER_OBSERVER, tny_folder_observer_init));
1133 foreach_add_item (gpointer header, gpointer user_data)
1135 tny_list_prepend (TNY_LIST (user_data),
1136 g_object_ref (G_OBJECT (header)));
1139 /* This is the method that looks for new messages in a folder */
1141 internal_folder_observer_update (TnyFolderObserver *self, TnyFolderChange *change)
1143 InternalFolderObserver *derived = (InternalFolderObserver *)self;
1145 TnyFolderChangeChanged changed;
1147 changed = tny_folder_change_get_changed (change);
1149 if (changed & TNY_FOLDER_CHANGE_CHANGED_ADDED_HEADERS) {
1152 /* Get added headers */
1153 list = tny_simple_list_new ();
1154 tny_folder_change_get_added_headers (change, list);
1156 /* Add them to the folder observer */
1157 tny_list_foreach (list, foreach_add_item,
1158 derived->new_headers);
1160 g_object_unref (G_OBJECT (list));
1165 internal_folder_observer_init (InternalFolderObserver *self)
1167 self->new_headers = tny_simple_list_new ();
1170 internal_folder_observer_finalize (GObject *object)
1172 InternalFolderObserver *self;
1174 self = (InternalFolderObserver *) object;
1175 g_object_unref (self->new_headers);
1177 G_OBJECT_CLASS (internal_folder_observer_parent_class)->finalize (object);
1180 tny_folder_observer_init (TnyFolderObserverIface *iface)
1182 iface->update_func = internal_folder_observer_update;
1185 internal_folder_observer_class_init (InternalFolderObserverClass *klass)
1187 GObjectClass *object_class;
1189 internal_folder_observer_parent_class = g_type_class_peek_parent (klass);
1190 object_class = (GObjectClass*) klass;
1191 object_class->finalize = internal_folder_observer_finalize;
1196 ModestMailOperation *mail_op;
1197 gchar *account_name;
1198 UpdateAccountCallback callback;
1203 TnyFolderObserver *inbox_observer;
1204 guint update_timeout;
1205 } UpdateAccountInfo;
1209 destroy_update_account_info (UpdateAccountInfo *info)
1211 if (info->update_timeout) {
1212 g_source_remove (info->update_timeout);
1213 info->update_timeout = 0;
1216 g_free (info->account_name);
1217 g_object_unref (info->folders);
1218 g_object_unref (info->mail_op);
1219 g_slice_free (UpdateAccountInfo, info);
1223 update_account_get_msg_async_cb (TnyFolder *folder,
1229 GetMsgInfo *msg_info = (GetMsgInfo *) user_data;
1231 /* Just delete the helper. Don't do anything with the new
1232 msg. There is also no need to check for errors */
1233 g_object_unref (msg_info->mail_op);
1234 g_object_unref (msg_info->header);
1235 g_slice_free (GetMsgInfo, msg_info);
1240 inbox_refreshed_cb (TnyFolder *inbox,
1245 UpdateAccountInfo *info;
1246 ModestMailOperationPrivate *priv;
1247 TnyIterator *new_headers_iter;
1248 GPtrArray *new_headers_array = NULL;
1249 gint max_size, retrieve_limit, i;
1250 ModestAccountMgr *mgr;
1251 gchar *retrieve_type = NULL;
1252 TnyList *new_headers = NULL;
1253 gboolean headers_only;
1254 TnyTransportAccount *transport_account;
1255 ModestTnySendQueue *send_queue;
1257 info = (UpdateAccountInfo *) user_data;
1258 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1259 mgr = modest_runtime_get_account_mgr ();
1261 if (canceled || err || !inbox) {
1262 /* Try to send anyway */
1266 /* Get the message max size */
1267 max_size = modest_conf_get_int (modest_runtime_get_conf (),
1268 MODEST_CONF_MSG_SIZE_LIMIT, NULL);
1270 max_size = G_MAXINT;
1272 max_size = max_size * KB;
1274 /* Create the new headers array. We need it to sort the
1275 new headers by date */
1276 new_headers_array = g_ptr_array_new ();
1277 new_headers_iter = tny_list_create_iterator (((InternalFolderObserver *) info->inbox_observer)->new_headers);
1278 while (!tny_iterator_is_done (new_headers_iter)) {
1279 TnyHeader *header = NULL;
1281 header = TNY_HEADER (tny_iterator_get_current (new_headers_iter));
1282 /* Apply per-message size limits */
1283 if (tny_header_get_message_size (header) < max_size)
1284 g_ptr_array_add (new_headers_array, g_object_ref (header));
1286 g_object_unref (header);
1287 tny_iterator_next (new_headers_iter);
1289 g_object_unref (new_headers_iter);
1290 tny_folder_remove_observer (inbox, info->inbox_observer);
1291 g_object_unref (info->inbox_observer);
1292 info->inbox_observer = NULL;
1294 if (new_headers_array->len == 0)
1297 /* Get per-account message amount retrieval limit */
1298 retrieve_limit = modest_account_mgr_get_retrieve_limit (mgr, info->account_name);
1299 if (retrieve_limit == 0)
1300 retrieve_limit = G_MAXINT;
1302 /* Get per-account retrieval type */
1303 retrieve_type = modest_account_mgr_get_retrieve_type (mgr, info->account_name);
1304 headers_only = !g_ascii_strcasecmp (retrieve_type, MODEST_ACCOUNT_RETRIEVE_VALUE_HEADERS_ONLY);
1305 g_free (retrieve_type);
1308 g_ptr_array_sort (new_headers_array, (GCompareFunc) compare_headers_by_date);
1310 /* TODO: Ask the user, instead of just failing,
1311 * showing mail_nc_msg_count_limit_exceeded, with 'Get
1312 * all' and 'Newest only' buttons. */
1313 if (new_headers_array->len > retrieve_limit) {
1317 if (!headers_only) {
1319 const gint msg_list_size = compute_message_array_size (new_headers_array);
1322 priv->total = MIN (new_headers_array->len, retrieve_limit);
1323 while (msg_num < priv->total) {
1324 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, msg_num));
1325 TnyFolder *folder = tny_header_get_folder (header);
1326 GetMsgInfo *msg_info;
1328 /* Create the message info */
1329 msg_info = g_slice_new0 (GetMsgInfo);
1330 msg_info->mail_op = g_object_ref (info->mail_op);
1331 msg_info->header = g_object_ref (header);
1332 msg_info->total_bytes = msg_list_size;
1334 /* Get message in an async way */
1335 tny_folder_get_msg_async (folder, header, update_account_get_msg_async_cb,
1336 get_msg_status_cb, msg_info);
1338 g_object_unref (folder);
1344 /* Copy the headers to a list and free the array */
1345 new_headers = tny_simple_list_new ();
1346 for (i=0; i < new_headers_array->len; i++) {
1347 TnyHeader *header = TNY_HEADER (g_ptr_array_index (new_headers_array, i));
1348 tny_list_append (new_headers, G_OBJECT (header));
1350 g_ptr_array_foreach (new_headers_array, (GFunc) g_object_unref, NULL);
1351 g_ptr_array_free (new_headers_array, FALSE);
1353 /* Update the last updated key */
1354 modest_account_mgr_set_last_updated (mgr, tny_account_get_id (priv->account), time (NULL));
1361 /* Get the transport account */
1362 transport_account = (TnyTransportAccount *)
1363 modest_tny_account_store_get_transport_account_for_open_connection (modest_runtime_get_account_store(),
1364 info->account_name);
1367 send_queue = modest_runtime_get_send_queue (transport_account);
1368 modest_tny_send_queue_try_to_send (send_queue);
1370 /* Check if the operation was a success */
1372 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1374 /* Set the account back to not busy */
1375 modest_account_mgr_set_account_busy (mgr, info->account_name, FALSE);
1377 /* Call the user callback */
1379 info->callback (info->mail_op, new_headers, info->user_data);
1381 /* Notify about operation end */
1382 modest_mail_operation_notify_end (info->mail_op);
1386 g_object_unref (new_headers);
1387 destroy_update_account_info (info);
1391 recurse_folders_async_cb (TnyFolderStore *folder_store,
1397 UpdateAccountInfo *info;
1398 ModestMailOperationPrivate *priv;
1400 info = (UpdateAccountInfo *) user_data;
1401 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
1403 if (err || canceled) {
1404 /* Try to continue anyway */
1406 TnyIterator *iter = tny_list_create_iterator (list);
1407 while (!tny_iterator_is_done (iter)) {
1408 TnyFolderStore *folder = (TnyFolderStore*) tny_iterator_get_current (iter);
1409 TnyList *folders = tny_simple_list_new ();
1411 /* Add to the list of all folders */
1412 tny_list_append (info->folders, (GObject *) folder);
1414 /* Add pending call */
1415 info->pending_calls++;
1417 tny_folder_store_get_folders_async (folder, folders, recurse_folders_async_cb,
1420 g_object_unref (G_OBJECT (folder));
1422 tny_iterator_next (iter);
1424 g_object_unref (G_OBJECT (iter));
1425 g_object_unref (G_OBJECT (list));
1428 /* Remove my own pending call */
1429 info->pending_calls--;
1431 /* This means that we have all the folders */
1432 if (info->pending_calls == 0) {
1433 TnyIterator *iter_all_folders;
1434 TnyFolder *inbox = NULL;
1436 iter_all_folders = tny_list_create_iterator (info->folders);
1438 /* Do a poke status over all folders */
1439 while (!tny_iterator_is_done (iter_all_folders) &&
1440 priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS) {
1441 TnyFolder *folder = NULL;
1443 folder = TNY_FOLDER (tny_iterator_get_current (iter_all_folders));
1445 if (tny_folder_get_folder_type (folder) == TNY_FOLDER_TYPE_INBOX) {
1446 /* Get a reference to the INBOX */
1447 inbox = g_object_ref (folder);
1449 /* Issue a poke status over the folder */
1451 tny_folder_poke_status (folder);
1454 /* Free and go to next */
1455 g_object_unref (folder);
1456 tny_iterator_next (iter_all_folders);
1458 g_object_unref (iter_all_folders);
1460 /* Stop the progress notification */
1461 g_source_remove (info->update_timeout);
1462 info->update_timeout = 0;
1464 /* Refresh the INBOX */
1466 /* Refresh the folder. Our observer receives
1467 * the new emails during folder refreshes, so
1468 * we can use observer->new_headers
1470 info->inbox_observer = g_object_new (internal_folder_observer_get_type (), NULL);
1471 tny_folder_add_observer (inbox, info->inbox_observer);
1473 /* Refresh the INBOX */
1474 tny_folder_refresh_async (inbox, inbox_refreshed_cb, NULL, info);
1475 g_object_unref (inbox);
1477 /* We could not perform the inbox refresh but
1478 we'll try to send mails anyway */
1479 inbox_refreshed_cb (inbox, FALSE, NULL, info);
1485 * Issues the "progress-changed" signal. The timer won't be removed,
1486 * so you must call g_source_remove to stop the signal emission
1489 timeout_notify_progress (gpointer data)
1491 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1492 ModestMailOperationState *state;
1494 state = modest_mail_operation_clone_state (mail_op);
1496 /* This is a GDK lock because we are an idle callback and
1497 * the handlers of this signal can contain Gtk+ code */
1499 gdk_threads_enter (); /* CHECKED */
1500 g_signal_emit (G_OBJECT (mail_op), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1501 gdk_threads_leave (); /* CHECKED */
1503 g_slice_free (ModestMailOperationState, state);
1509 modest_mail_operation_update_account (ModestMailOperation *self,
1510 const gchar *account_name,
1512 UpdateAccountCallback callback,
1515 UpdateAccountInfo *info = NULL;
1516 ModestMailOperationPrivate *priv = NULL;
1517 ModestTnyAccountStore *account_store = NULL;
1518 TnyStoreAccount *store_account = NULL;
1521 /* Init mail operation */
1522 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1525 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1526 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1528 /* Get the store account */
1529 account_store = modest_runtime_get_account_store ();
1530 store_account = (TnyStoreAccount *)
1531 modest_tny_account_store_get_server_account (account_store,
1533 TNY_ACCOUNT_TYPE_STORE);
1534 priv->account = g_object_ref (store_account);
1536 /* Create the helper object */
1537 info = g_slice_new0 (UpdateAccountInfo);
1538 info->pending_calls = 1;
1539 info->folders = tny_simple_list_new ();
1540 info->mail_op = g_object_ref (self);
1541 info->poke_all = poke_all;
1542 info->account_name = g_strdup (account_name);
1543 info->callback = callback;
1544 info->user_data = user_data;
1545 info->update_timeout = g_timeout_add (250, timeout_notify_progress, self);
1547 /* Set account busy */
1548 modest_account_mgr_set_account_busy (modest_runtime_get_account_mgr (), account_name, TRUE);
1549 modest_mail_operation_notify_start (self);
1551 /* Get all folders and continue in the callback */
1552 folders = tny_simple_list_new ();
1553 tny_folder_store_get_folders_async (TNY_FOLDER_STORE (store_account),
1554 folders, recurse_folders_async_cb,
1559 * Used to notify the queue from the main
1560 * loop. We call it inside an idle call to achieve that
1563 idle_notify_queue (gpointer data)
1565 ModestMailOperation *mail_op = MODEST_MAIL_OPERATION (data);
1567 gdk_threads_enter ();
1568 modest_mail_operation_notify_end (mail_op);
1569 gdk_threads_leave ();
1570 g_object_unref (mail_op);
1576 compare_headers_by_date (gconstpointer a,
1579 TnyHeader **header1, **header2;
1580 time_t sent1, sent2;
1582 header1 = (TnyHeader **) a;
1583 header2 = (TnyHeader **) b;
1585 sent1 = tny_header_get_date_sent (*header1);
1586 sent2 = tny_header_get_date_sent (*header2);
1588 /* We want the most recent ones (greater time_t) at the
1597 /* ******************************************************************* */
1598 /* ************************** STORE ACTIONS ************************* */
1599 /* ******************************************************************* */
1603 modest_mail_operation_create_folder (ModestMailOperation *self,
1604 TnyFolderStore *parent,
1607 ModestMailOperationPrivate *priv;
1608 TnyFolder *new_folder = NULL;
1610 g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent), NULL);
1611 g_return_val_if_fail (name, NULL);
1613 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1614 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1615 priv->account = (TNY_IS_ACCOUNT (parent)) ?
1616 g_object_ref (parent) :
1617 modest_tny_folder_get_account (TNY_FOLDER (parent));
1619 /* Check for already existing folder */
1620 if (modest_tny_folder_has_subfolder_with_name (parent, name, TRUE)) {
1621 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1622 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1623 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1624 _CS("ckdg_ib_folder_already_exists"));
1628 if (TNY_IS_FOLDER (parent)) {
1629 /* Check folder rules */
1630 ModestTnyFolderRules rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1631 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
1632 /* Set status failed and set an error */
1633 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1634 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1635 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1636 _("mail_in_ui_folder_create_error"));
1640 if (!strcmp (name, " ") || strchr (name, '/')) {
1641 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1642 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1643 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1644 _("mail_in_ui_folder_create_error"));
1648 /* Create the folder */
1649 modest_mail_operation_notify_start (self);
1650 new_folder = tny_folder_store_create_folder (parent, name, &(priv->error));
1651 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1653 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1656 /* Notify about operation end */
1657 modest_mail_operation_notify_end (self);
1663 modest_mail_operation_remove_folder (ModestMailOperation *self,
1665 gboolean remove_to_trash)
1667 TnyAccount *account;
1668 ModestMailOperationPrivate *priv;
1669 ModestTnyFolderRules rules;
1671 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1672 g_return_if_fail (TNY_IS_FOLDER (folder));
1674 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1676 /* Check folder rules */
1677 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1678 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_DELETABLE) {
1679 /* Set status failed and set an error */
1680 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1681 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1682 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1683 _("mail_in_ui_folder_delete_error"));
1687 /* Get the account */
1688 account = modest_tny_folder_get_account (folder);
1689 priv->account = g_object_ref(account);
1690 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
1692 /* Delete folder or move to trash */
1693 if (remove_to_trash) {
1694 TnyFolder *trash_folder = NULL;
1695 trash_folder = modest_tny_account_get_special_folder (account,
1696 TNY_FOLDER_TYPE_TRASH);
1697 /* TODO: error_handling */
1699 modest_mail_operation_notify_start (self);
1700 modest_mail_operation_xfer_folder (self, folder,
1701 TNY_FOLDER_STORE (trash_folder),
1703 g_object_unref (trash_folder);
1706 TnyFolderStore *parent = tny_folder_get_folder_store (folder);
1708 modest_mail_operation_notify_start (self);
1709 tny_folder_store_remove_folder (parent, folder, &(priv->error));
1710 CHECK_EXCEPTION (priv, MODEST_MAIL_OPERATION_STATUS_FAILED);
1713 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1715 g_object_unref (parent);
1717 g_warning ("%s: could not get parent folder", __FUNCTION__);
1719 g_object_unref (G_OBJECT (account));
1722 /* Notify about operation end */
1723 modest_mail_operation_notify_end (self);
1727 transfer_folder_status_cb (GObject *obj,
1731 ModestMailOperation *self;
1732 ModestMailOperationPrivate *priv;
1733 ModestMailOperationState *state;
1734 XFerMsgAsyncHelper *helper;
1736 g_return_if_fail (status != NULL);
1737 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_COPY_FOLDER);
1739 helper = (XFerMsgAsyncHelper *) user_data;
1740 g_return_if_fail (helper != NULL);
1742 self = helper->mail_op;
1743 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1745 priv->done = status->position;
1746 priv->total = status->of_total;
1748 state = modest_mail_operation_clone_state (self);
1750 /* This is not a GDK lock because we are a Tinymail callback
1751 * which is already GDK locked by Tinymail */
1753 /* no gdk_threads_enter (), CHECKED */
1755 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
1757 /* no gdk_threads_leave (), CHECKED */
1759 g_slice_free (ModestMailOperationState, state);
1764 transfer_folder_cb (TnyFolder *folder,
1766 TnyFolderStore *into,
1767 TnyFolder *new_folder,
1771 XFerMsgAsyncHelper *helper;
1772 ModestMailOperation *self = NULL;
1773 ModestMailOperationPrivate *priv = NULL;
1775 helper = (XFerMsgAsyncHelper *) user_data;
1776 g_return_if_fail (helper != NULL);
1778 self = helper->mail_op;
1779 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
1782 priv->error = g_error_copy (err);
1784 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1785 } else if (cancelled) {
1786 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
1787 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1788 MODEST_MAIL_OPERATION_ERROR_OPERATION_CANCELED,
1789 _("Transference of %s was cancelled."),
1790 tny_folder_get_name (folder));
1793 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
1796 /* Notify about operation end */
1797 modest_mail_operation_notify_end (self);
1799 /* If user defined callback function was defined, call it */
1800 if (helper->user_callback) {
1802 /* This is not a GDK lock because we are a Tinymail callback
1803 * which is already GDK locked by Tinymail */
1805 /* no gdk_threads_enter (), CHECKED */
1806 helper->user_callback (self, helper->user_data);
1807 /* no gdk_threads_leave () , CHECKED */
1811 g_object_unref (helper->mail_op);
1812 g_slice_free (XFerMsgAsyncHelper, helper);
1817 * This function checks if the new name is a valid name for our local
1818 * folders account. The new name could not be the same than then name
1819 * of any of the mandatory local folders
1821 * We can not rely on tinymail because tinymail does not check the
1822 * name of the virtual folders that the account could have in the case
1823 * that we're doing a rename (because it directly calls Camel which
1824 * knows nothing about our virtual folders).
1826 * In the case of an actual copy/move (i.e. move/copy a folder between
1827 * accounts) tinymail uses the tny_folder_store_create_account which
1828 * is reimplemented by our ModestTnyLocalFoldersAccount that indeed
1829 * checks the new name of the folder, so this call in that case
1830 * wouldn't be needed. *But* NOTE that if tinymail changes its
1831 * implementation (if folder transfers within the same account is no
1832 * longer implemented as a rename) this call will allow Modest to work
1835 * If the new name is not valid, this function will set the status to
1836 * failed and will set also an error in the mail operation
1839 new_name_valid_if_local_account (ModestMailOperationPrivate *priv,
1840 TnyFolderStore *into,
1841 const gchar *new_name)
1843 if (TNY_IS_ACCOUNT (into) &&
1844 modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (into)) &&
1845 modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (into),
1847 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1848 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1849 MODEST_MAIL_OPERATION_ERROR_FOLDER_EXISTS,
1850 _CS("ckdg_ib_folder_already_exists"));
1857 modest_mail_operation_xfer_folder (ModestMailOperation *self,
1859 TnyFolderStore *parent,
1860 gboolean delete_original,
1861 XferAsyncUserCallback user_callback,
1864 ModestMailOperationPrivate *priv = NULL;
1865 ModestTnyFolderRules parent_rules = 0, rules;
1866 XFerMsgAsyncHelper *helper = NULL;
1867 const gchar *folder_name = NULL;
1868 const gchar *error_msg;
1870 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1871 g_return_if_fail (TNY_IS_FOLDER (folder));
1872 g_return_if_fail (TNY_IS_FOLDER_STORE (parent));
1874 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1875 folder_name = tny_folder_get_name (folder);
1877 /* Set the error msg */
1878 error_msg = _("mail_in_ui_folder_move_target_error");
1880 /* Get account and set it into mail_operation */
1881 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
1882 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1883 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
1885 /* Get folder rules */
1886 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1887 if (TNY_IS_FOLDER (parent))
1888 parent_rules = modest_tny_folder_get_rules (TNY_FOLDER (parent));
1890 /* Apply operation constraints */
1891 if ((gpointer) parent == (gpointer) folder ||
1892 (!TNY_IS_FOLDER_STORE (parent)) ||
1893 (rules & MODEST_FOLDER_RULES_FOLDER_NON_MOVEABLE)) {
1896 } else if (TNY_IS_FOLDER (parent) &&
1897 (parent_rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE)) {
1901 } else if (TNY_IS_FOLDER (parent) &&
1902 TNY_IS_FOLDER_STORE (folder) &&
1903 modest_tny_folder_is_ancestor (TNY_FOLDER (parent),
1904 TNY_FOLDER_STORE (folder))) {
1905 /* Do not move a parent into a child */
1907 } else if (TNY_IS_FOLDER_STORE (parent) &&
1908 modest_tny_folder_has_subfolder_with_name (parent, folder_name, TRUE)) {
1909 /* Check that the new folder name is not used by any
1912 } else if (!(new_name_valid_if_local_account (priv, parent, folder_name))) {
1913 /* Check that the new folder name is not used by any
1914 special local folder */
1917 /* Create the helper */
1918 helper = g_slice_new0 (XFerMsgAsyncHelper);
1919 helper->mail_op = g_object_ref (self);
1920 helper->dest_folder = NULL;
1921 helper->headers = NULL;
1922 helper->user_callback = user_callback;
1923 helper->user_data = user_data;
1925 /* Move/Copy folder */
1926 modest_mail_operation_notify_start (self);
1927 tny_folder_copy_async (folder,
1929 tny_folder_get_name (folder),
1932 transfer_folder_status_cb,
1938 /* Set status failed and set an error */
1939 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1940 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1941 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1944 /* Call the user callback if exists */
1946 user_callback (self, user_data);
1948 /* Notify the queue */
1949 modest_mail_operation_notify_end (self);
1953 modest_mail_operation_rename_folder (ModestMailOperation *self,
1957 ModestMailOperationPrivate *priv;
1958 ModestTnyFolderRules rules;
1959 XFerMsgAsyncHelper *helper;
1961 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
1962 g_return_if_fail (TNY_IS_FOLDER_STORE (folder));
1963 g_return_if_fail (name);
1965 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
1967 /* Get account and set it into mail_operation */
1968 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
1969 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
1971 /* Check folder rules */
1972 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
1973 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_RENAMEABLE) {
1974 /* Set status failed and set an error */
1975 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1976 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1977 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1978 _("FIXME: unable to rename"));
1980 /* Notify about operation end */
1981 modest_mail_operation_notify_end (self);
1982 } else if (!strcmp (name, " ") || strchr (name, '/')) {
1983 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
1984 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
1985 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
1986 _("FIXME: unable to rename"));
1987 /* Notify about operation end */
1988 modest_mail_operation_notify_end (self);
1990 TnyFolderStore *into;
1992 into = tny_folder_get_folder_store (folder);
1994 /* Check that the new folder name is not used by any
1995 special local folder */
1996 if (new_name_valid_if_local_account (priv, into, name)) {
1997 /* Create the helper */
1998 helper = g_slice_new0 (XFerMsgAsyncHelper);
1999 helper->mail_op = g_object_ref(self);
2000 helper->dest_folder = NULL;
2001 helper->headers = NULL;
2002 helper->user_callback = NULL;
2003 helper->user_data = NULL;
2005 /* Rename. Camel handles folder subscription/unsubscription */
2006 modest_mail_operation_notify_start (self);
2007 tny_folder_copy_async (folder, into, name, TRUE,
2009 transfer_folder_status_cb,
2012 modest_mail_operation_notify_end (self);
2014 g_object_unref (into);
2018 /* ******************************************************************* */
2019 /* ************************** MSG ACTIONS ************************* */
2020 /* ******************************************************************* */
2023 modest_mail_operation_get_msg (ModestMailOperation *self,
2025 GetMsgAsyncUserCallback user_callback,
2028 GetMsgInfo *helper = NULL;
2030 ModestMailOperationPrivate *priv;
2032 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2033 g_return_if_fail (TNY_IS_HEADER (header));
2035 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2036 folder = tny_header_get_folder (header);
2038 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2042 /* Get account and set it into mail_operation */
2043 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2045 /* Check for cached messages */
2046 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_CACHED)
2047 priv->op_type = MODEST_MAIL_OPERATION_TYPE_OPEN;
2049 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2051 /* Create the helper */
2052 helper = g_slice_new0 (GetMsgInfo);
2053 helper->header = g_object_ref (header);
2054 helper->mail_op = g_object_ref (self);
2055 helper->user_callback = user_callback;
2056 helper->user_data = user_data;
2057 helper->destroy_notify = NULL;
2058 helper->last_total_bytes = 0;
2059 helper->sum_total_bytes = 0;
2060 helper->total_bytes = tny_header_get_message_size (header);
2062 modest_mail_operation_notify_start (self);
2063 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, helper);
2065 g_object_unref (G_OBJECT (folder));
2069 get_msg_status_cb (GObject *obj,
2073 GetMsgInfo *helper = NULL;
2075 g_return_if_fail (status != NULL);
2076 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_GET_MSG);
2078 helper = (GetMsgInfo *) user_data;
2079 g_return_if_fail (helper != NULL);
2081 /* Notify progress */
2082 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2083 &(helper->sum_total_bytes), helper->total_bytes, FALSE);
2087 get_msg_async_cb (TnyFolder *folder,
2093 GetMsgInfo *info = NULL;
2094 ModestMailOperationPrivate *priv = NULL;
2097 info = (GetMsgInfo *) user_data;
2099 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (info->mail_op);
2101 finished = (priv->done == priv->total) ? TRUE : FALSE;
2104 if (canceled || err) {
2105 priv->status = MODEST_MAIL_OPERATION_STATUS_FINISHED_WITH_ERRORS;
2107 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2108 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2111 /* Set the success status before calling the user callback */
2112 if (finished && priv->status == MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS)
2113 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2117 /* Call the user callback */
2118 if (info->user_callback)
2119 info->user_callback (info->mail_op, info->header, canceled,
2120 msg, err, info->user_data);
2122 /* Notify about operation end if this is the last callback */
2124 /* Free user data */
2125 if (info->destroy_notify)
2126 info->destroy_notify (info->user_data);
2128 /* Notify about operation end */
2129 modest_mail_operation_notify_end (info->mail_op);
2133 g_object_unref (info->header);
2134 g_object_unref (info->mail_op);
2135 g_slice_free (GetMsgInfo, info);
2139 modest_mail_operation_get_msgs_full (ModestMailOperation *self,
2140 TnyList *header_list,
2141 GetMsgAsyncUserCallback user_callback,
2143 GDestroyNotify notify)
2145 ModestMailOperationPrivate *priv = NULL;
2146 gboolean size_ok = TRUE;
2148 TnyIterator *iter = NULL;
2150 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2152 /* Init mail operation */
2153 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2154 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2155 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2157 priv->total = tny_list_get_length(header_list);
2159 /* Get account and set it into mail_operation */
2160 if (tny_list_get_length (header_list) >= 1) {
2161 iter = tny_list_create_iterator (header_list);
2162 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2164 TnyFolder *folder = tny_header_get_folder (header);
2166 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2167 g_object_unref (folder);
2169 g_object_unref (header);
2172 if (tny_list_get_length (header_list) == 1) {
2173 g_object_unref (iter);
2178 /* Get msg size limit */
2179 max_size = modest_conf_get_int (modest_runtime_get_conf (),
2180 MODEST_CONF_MSG_SIZE_LIMIT,
2183 g_clear_error (&(priv->error));
2184 max_size = G_MAXINT;
2186 max_size = max_size * KB;
2189 /* Check message size limits. If there is only one message
2190 always retrieve it */
2192 while (!tny_iterator_is_done (iter) && size_ok) {
2193 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2195 if (tny_header_get_message_size (header) >= max_size)
2197 g_object_unref (header);
2200 tny_iterator_next (iter);
2202 g_object_unref (iter);
2206 const gint msg_list_size = compute_message_list_size (header_list);
2208 modest_mail_operation_notify_start (self);
2209 iter = tny_list_create_iterator (header_list);
2210 while (!tny_iterator_is_done (iter)) {
2211 GetMsgInfo *msg_info = NULL;
2212 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2213 TnyFolder *folder = tny_header_get_folder (header);
2215 /* Create the message info */
2216 msg_info = g_slice_new0 (GetMsgInfo);
2217 msg_info->mail_op = g_object_ref (self);
2218 msg_info->header = g_object_ref (header);
2219 msg_info->user_callback = user_callback;
2220 msg_info->user_data = user_data;
2221 msg_info->destroy_notify = notify;
2222 msg_info->last_total_bytes = 0;
2223 msg_info->sum_total_bytes = 0;
2224 msg_info->total_bytes = msg_list_size;
2226 /* The callback will call it per each header */
2227 tny_folder_get_msg_async (folder, header, get_msg_async_cb, get_msg_status_cb, msg_info);
2229 /* Free and go on */
2230 g_object_unref (header);
2231 g_object_unref (folder);
2232 tny_iterator_next (iter);
2234 g_object_unref (iter);
2236 /* Set status failed and set an error */
2237 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2238 /* FIXME: the error msg is different for pop */
2239 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2240 MODEST_MAIL_OPERATION_ERROR_MESSAGE_SIZE_LIMIT,
2241 _("emev_ni_ui_imap_msg_size_exceed_error"));
2242 /* Remove from queue and free resources */
2243 modest_mail_operation_notify_end (self);
2251 modest_mail_operation_remove_msg (ModestMailOperation *self,
2253 gboolean remove_to_trash /*ignored*/)
2256 ModestMailOperationPrivate *priv;
2258 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2259 g_return_if_fail (TNY_IS_HEADER (header));
2261 if (remove_to_trash)
2262 g_warning ("remove to trash is not implemented");
2264 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2265 folder = tny_header_get_folder (header);
2267 /* Get account and set it into mail_operation */
2268 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2269 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2270 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2272 /* remove message from folder */
2273 tny_folder_remove_msg (folder, header, &(priv->error));
2275 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2276 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2278 modest_mail_operation_notify_start (self);
2280 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2281 TNY_IS_CAMEL_POP_FOLDER (folder))
2282 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> dont expunge */
2284 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2290 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2292 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2295 g_object_unref (G_OBJECT (folder));
2297 /* Notify about operation end */
2298 modest_mail_operation_notify_end (self);
2302 modest_mail_operation_remove_msgs (ModestMailOperation *self,
2304 gboolean remove_to_trash /*ignored*/)
2307 ModestMailOperationPrivate *priv;
2308 TnyIterator *iter = NULL;
2309 TnyHeader *header = NULL;
2310 TnyList *remove_headers = NULL;
2311 TnyFolderType folder_type = TNY_FOLDER_TYPE_UNKNOWN;
2313 g_return_if_fail (MODEST_IS_MAIL_OPERATION (self));
2314 g_return_if_fail (TNY_IS_LIST (headers));
2316 if (remove_to_trash)
2317 g_warning ("remove to trash is not implemented");
2319 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2321 remove_headers = g_object_ref(headers);
2323 /* Get folder from first header and sync it */
2324 iter = tny_list_create_iterator (headers);
2325 header = TNY_HEADER (tny_iterator_get_current (iter));
2326 folder = tny_header_get_folder (header);
2328 /* Don't remove messages that are being sent */
2329 if (modest_tny_folder_is_local_folder (folder)) {
2330 folder_type = modest_tny_folder_get_local_or_mmc_folder_type (folder);
2332 if (folder_type == TNY_FOLDER_TYPE_OUTBOX) {
2333 TnyTransportAccount *traccount = NULL;
2334 ModestTnyAccountStore *accstore = modest_runtime_get_account_store();
2335 traccount = modest_tny_account_store_get_transport_account_from_outbox_header(accstore, header);
2337 ModestTnySendQueueStatus status;
2338 ModestTnySendQueue *send_queue = modest_runtime_get_send_queue(traccount);
2339 TnyIterator *iter = tny_list_create_iterator(headers);
2340 g_object_unref(remove_headers);
2341 remove_headers = TNY_LIST(tny_simple_list_new());
2342 while (!tny_iterator_is_done(iter)) {
2344 TnyHeader *hdr = TNY_HEADER(tny_iterator_get_current(iter));
2345 msg_id = modest_tny_send_queue_get_msg_id (hdr);
2346 status = modest_tny_send_queue_get_msg_status(send_queue, msg_id);
2347 if (status != MODEST_TNY_SEND_QUEUE_SENDING) {
2348 tny_list_append(remove_headers, G_OBJECT(hdr));
2350 g_object_unref(hdr);
2352 tny_iterator_next(iter);
2354 g_object_unref(iter);
2355 g_object_unref(traccount);
2359 /* Get account and set it into mail_operation */
2360 priv->account = modest_tny_folder_get_account (TNY_FOLDER(folder));
2361 priv->op_type = MODEST_MAIL_OPERATION_TYPE_DELETE;
2362 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2364 /* remove message from folder */
2365 modest_mail_operation_notify_start (self);
2367 tny_folder_remove_msgs (folder, remove_headers, &(priv->error));
2369 if (TNY_IS_CAMEL_IMAP_FOLDER (folder) ||
2370 TNY_IS_CAMEL_POP_FOLDER (folder))
2371 tny_folder_sync_async(folder, FALSE, NULL, NULL, NULL); /* FALSE --> don't expunge */
2374 tny_folder_sync_async(folder, TRUE, NULL, NULL, NULL); /* TRUE --> expunge */
2380 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2382 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2385 g_object_unref (remove_headers);
2386 g_object_unref (header);
2387 g_object_unref (iter);
2388 g_object_unref (G_OBJECT (folder));
2390 /* Notify about operation end */
2391 modest_mail_operation_notify_end (self);
2395 notify_progress_of_multiple_messages (ModestMailOperation *self,
2397 gint *last_total_bytes,
2398 gint *sum_total_bytes,
2400 gboolean increment_done)
2402 ModestMailOperationPrivate *priv;
2403 ModestMailOperationState *state;
2404 gboolean is_num_bytes;
2406 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2408 /* We know that tinymail sends us information about
2409 transferred bytes with this particular message */
2410 is_num_bytes = (g_ascii_strcasecmp (status->message, "Retrieving message") == 0);
2412 state = modest_mail_operation_clone_state (self);
2413 if (is_num_bytes && !((status->position == 1) && (status->of_total == 100))) {
2414 /* We know that we're in a different message when the
2415 total number of bytes to transfer is different. Of
2416 course it could fail if we're transferring messages
2417 of the same size, but this is a workarround */
2418 if (status->of_total != *last_total_bytes) {
2419 /* We need to increment the done when there is
2420 no information about each individual
2421 message, we need to do this in message
2422 transfers, and we don't do it for getting
2426 *sum_total_bytes += *last_total_bytes;
2427 *last_total_bytes = status->of_total;
2429 state->bytes_done += status->position + *sum_total_bytes;
2430 state->bytes_total = total_bytes;
2432 /* Notify the status change. Only notify about changes
2433 referred to bytes */
2434 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL],
2438 g_slice_free (ModestMailOperationState, state);
2442 transfer_msgs_status_cb (GObject *obj,
2446 XFerMsgAsyncHelper *helper;
2448 g_return_if_fail (status != NULL);
2449 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_XFER_MSGS);
2451 helper = (XFerMsgAsyncHelper *) user_data;
2452 g_return_if_fail (helper != NULL);
2454 /* Notify progress */
2455 notify_progress_of_multiple_messages (helper->mail_op, status, &(helper->last_total_bytes),
2456 &(helper->sum_total_bytes), helper->total_bytes, TRUE);
2461 transfer_msgs_cb (TnyFolder *folder, gboolean cancelled, GError *err, gpointer user_data)
2463 XFerMsgAsyncHelper *helper;
2464 ModestMailOperation *self;
2465 ModestMailOperationPrivate *priv;
2466 TnyIterator *iter = NULL;
2467 TnyHeader *header = NULL;
2469 helper = (XFerMsgAsyncHelper *) user_data;
2470 self = helper->mail_op;
2472 priv = MODEST_MAIL_OPERATION_GET_PRIVATE (self);
2475 priv->error = g_error_copy (err);
2477 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2478 } else if (cancelled) {
2479 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2480 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2481 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2482 _("Error trying to refresh the contents of %s"),
2483 tny_folder_get_name (folder));
2486 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2488 /* Update folder counts */
2489 tny_folder_poke_status (folder);
2490 tny_folder_poke_status (helper->dest_folder);
2494 /* Mark headers as deleted and seen */
2495 if ((helper->delete) &&
2496 (priv->status == MODEST_MAIL_OPERATION_STATUS_SUCCESS)) {
2497 iter = tny_list_create_iterator (helper->headers);
2498 while (!tny_iterator_is_done (iter)) {
2499 header = TNY_HEADER (tny_iterator_get_current (iter));
2500 tny_header_set_flag (header, TNY_HEADER_FLAG_DELETED);
2501 tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
2502 g_object_unref (header);
2504 tny_iterator_next (iter);
2510 /* Notify about operation end */
2511 modest_mail_operation_notify_end (self);
2513 /* If user defined callback function was defined, call it */
2514 if (helper->user_callback) {
2515 /* This is not a GDK lock because we are a Tinymail callback and
2516 * Tinymail already acquires the Gdk lock */
2518 /* no gdk_threads_enter (), CHECKED */
2519 helper->user_callback (self, helper->user_data);
2520 /* no gdk_threads_leave (), CHECKED */
2524 if (helper->headers)
2525 g_object_unref (helper->headers);
2526 if (helper->dest_folder)
2527 g_object_unref (helper->dest_folder);
2528 if (helper->mail_op)
2529 g_object_unref (helper->mail_op);
2531 g_object_unref (folder);
2533 g_object_unref (iter);
2534 g_slice_free (XFerMsgAsyncHelper, helper);
2538 compute_message_list_size (TnyList *headers)
2543 iter = tny_list_create_iterator (headers);
2544 while (!tny_iterator_is_done (iter)) {
2545 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
2546 size += tny_header_get_message_size (header);
2547 g_object_unref (header);
2548 tny_iterator_next (iter);
2550 g_object_unref (iter);
2556 compute_message_array_size (GPtrArray *headers)
2561 for (i = 0; i < headers->len; i++) {
2562 TnyHeader *header = TNY_HEADER (g_ptr_array_index (headers, i));
2563 size += tny_header_get_message_size (header);
2571 modest_mail_operation_xfer_msgs (ModestMailOperation *self,
2574 gboolean delete_original,
2575 XferAsyncUserCallback user_callback,
2578 ModestMailOperationPrivate *priv = NULL;
2579 TnyIterator *iter = NULL;
2580 TnyFolder *src_folder = NULL;
2581 XFerMsgAsyncHelper *helper = NULL;
2582 TnyHeader *header = NULL;
2583 ModestTnyFolderRules rules = 0;
2585 g_return_if_fail (self && MODEST_IS_MAIL_OPERATION (self));
2586 g_return_if_fail (headers && TNY_IS_LIST (headers));
2587 g_return_if_fail (folder && TNY_IS_FOLDER (folder));
2589 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2590 priv->total = tny_list_get_length (headers);
2592 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2593 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2595 /* Apply folder rules */
2596 rules = modest_tny_folder_get_rules (TNY_FOLDER (folder));
2597 if (rules & MODEST_FOLDER_RULES_FOLDER_NON_WRITEABLE) {
2598 /* Set status failed and set an error */
2599 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2600 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2601 MODEST_MAIL_OPERATION_ERROR_FOLDER_RULES,
2602 _CS("ckct_ib_unable_to_paste_here"));
2603 /* Notify the queue */
2604 modest_mail_operation_notify_end (self);
2608 /* Get source folder */
2609 iter = tny_list_create_iterator (headers);
2610 header = TNY_HEADER (tny_iterator_get_current (iter));
2612 src_folder = tny_header_get_folder (header);
2613 g_object_unref (header);
2615 g_object_unref (iter);
2617 if (src_folder == NULL) {
2618 /* Notify the queue */
2619 modest_mail_operation_notify_end (self);
2621 g_warning ("%s: cannot find folder from header", __FUNCTION__);
2626 /* Check folder source and destination */
2627 if (src_folder == folder) {
2628 /* Set status failed and set an error */
2629 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2630 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2631 MODEST_MAIL_OPERATION_ERROR_BAD_PARAMETER,
2632 _("mail_in_ui_folder_copy_target_error"));
2634 /* Notify the queue */
2635 modest_mail_operation_notify_end (self);
2638 g_object_unref (src_folder);
2642 /* Create the helper */
2643 helper = g_slice_new0 (XFerMsgAsyncHelper);
2644 helper->mail_op = g_object_ref(self);
2645 helper->dest_folder = g_object_ref(folder);
2646 helper->headers = g_object_ref(headers);
2647 helper->user_callback = user_callback;
2648 helper->user_data = user_data;
2649 helper->delete = delete_original;
2650 helper->last_total_bytes = 0;
2651 helper->sum_total_bytes = 0;
2652 helper->total_bytes = compute_message_list_size (headers);
2654 /* Get account and set it into mail_operation */
2655 priv->account = modest_tny_folder_get_account (src_folder);
2657 /* Transfer messages */
2658 modest_mail_operation_notify_start (self);
2659 tny_folder_transfer_msgs_async (src_folder,
2664 transfer_msgs_status_cb,
2670 on_refresh_folder (TnyFolder *folder,
2675 RefreshAsyncHelper *helper = NULL;
2676 ModestMailOperation *self = NULL;
2677 ModestMailOperationPrivate *priv = NULL;
2679 helper = (RefreshAsyncHelper *) user_data;
2680 self = helper->mail_op;
2681 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2683 g_return_if_fail(priv!=NULL);
2686 priv->error = g_error_copy (error);
2687 priv->status = MODEST_MAIL_OPERATION_STATUS_FAILED;
2692 priv->status = MODEST_MAIL_OPERATION_STATUS_CANCELED;
2693 g_set_error (&(priv->error), MODEST_MAIL_OPERATION_ERROR,
2694 MODEST_MAIL_OPERATION_ERROR_ITEM_NOT_FOUND,
2695 _("Error trying to refresh the contents of %s"),
2696 tny_folder_get_name (folder));
2700 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2703 /* Call user defined callback, if it exists */
2704 if (helper->user_callback) {
2706 /* This is not a GDK lock because we are a Tinymail callback and
2707 * Tinymail already acquires the Gdk lock */
2708 helper->user_callback (self, folder, helper->user_data);
2712 g_slice_free (RefreshAsyncHelper, helper);
2714 /* Notify about operation end */
2715 modest_mail_operation_notify_end (self);
2716 g_object_unref(self);
2720 on_refresh_folder_status_update (GObject *obj,
2724 RefreshAsyncHelper *helper = NULL;
2725 ModestMailOperation *self = NULL;
2726 ModestMailOperationPrivate *priv = NULL;
2727 ModestMailOperationState *state;
2729 g_return_if_fail (user_data != NULL);
2730 g_return_if_fail (status != NULL);
2731 g_return_if_fail (status->code == TNY_FOLDER_STATUS_CODE_REFRESH);
2733 helper = (RefreshAsyncHelper *) user_data;
2734 self = helper->mail_op;
2735 g_return_if_fail (MODEST_IS_MAIL_OPERATION(self));
2737 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2739 priv->done = status->position;
2740 priv->total = status->of_total;
2742 state = modest_mail_operation_clone_state (self);
2744 /* This is not a GDK lock because we are a Tinymail callback and
2745 * Tinymail already acquires the Gdk lock */
2746 g_signal_emit (G_OBJECT (self), signals[PROGRESS_CHANGED_SIGNAL], 0, state, NULL);
2748 g_slice_free (ModestMailOperationState, state);
2752 modest_mail_operation_refresh_folder (ModestMailOperation *self,
2754 RefreshAsyncUserCallback user_callback,
2757 ModestMailOperationPrivate *priv = NULL;
2758 RefreshAsyncHelper *helper = NULL;
2760 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2762 priv->status = MODEST_MAIL_OPERATION_STATUS_IN_PROGRESS;
2764 /* Get account and set it into mail_operation */
2765 priv->account = modest_tny_folder_get_account (folder);
2766 priv->op_type = MODEST_MAIL_OPERATION_TYPE_RECEIVE;
2768 /* Create the helper */
2769 helper = g_slice_new0 (RefreshAsyncHelper);
2770 helper->mail_op = g_object_ref(self);
2771 helper->user_callback = user_callback;
2772 helper->user_data = user_data;
2774 /* Refresh the folder. TODO: tinymail could issue a status
2775 updates before the callback call then this could happen. We
2776 must review the design */
2777 modest_mail_operation_notify_start (self);
2778 tny_folder_refresh_async (folder,
2780 on_refresh_folder_status_update,
2786 modest_mail_operation_notify_start (ModestMailOperation *self)
2788 ModestMailOperationPrivate *priv = NULL;
2790 g_return_if_fail (self);
2792 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2794 /* Ensure that all the fields are filled correctly */
2795 g_return_if_fail (priv->op_type != MODEST_MAIL_OPERATION_TYPE_UNKNOWN);
2797 /* Notify the observers about the mail operation. We do not
2798 wrapp this emission because we assume that this function is
2799 always called from within the main lock */
2800 g_signal_emit (G_OBJECT (self), signals[OPERATION_STARTED_SIGNAL], 0, NULL);
2805 * It's used by the mail operation queue to notify the observers
2806 * attached to that signal that the operation finished. We need to use
2807 * that because tinymail does not give us the progress of a given
2808 * operation when it finishes (it directly calls the operation
2812 modest_mail_operation_notify_end (ModestMailOperation *self)
2814 ModestMailOperationPrivate *priv = NULL;
2816 g_return_if_fail (self);
2818 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2820 /* Notify the observers about the mail operation end. We do
2821 not wrapp this emission because we assume that this
2822 function is always called from within the main lock */
2823 g_signal_emit (G_OBJECT (self), signals[OPERATION_FINISHED_SIGNAL], 0, NULL);
2825 /* Remove the error user data */
2826 if (priv->error_checking_user_data && priv->error_checking_user_data_destroyer)
2827 priv->error_checking_user_data_destroyer (priv->error_checking_user_data);
2831 modest_mail_operation_get_account (ModestMailOperation *self)
2833 ModestMailOperationPrivate *priv = NULL;
2835 g_return_val_if_fail (self, NULL);
2837 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2839 return (priv->account) ? g_object_ref (priv->account) : NULL;
2843 modest_mail_operation_noop (ModestMailOperation *self)
2845 ModestMailOperationPrivate *priv = NULL;
2847 g_return_if_fail (self);
2849 priv = MODEST_MAIL_OPERATION_GET_PRIVATE(self);
2850 priv->status = MODEST_MAIL_OPERATION_STATUS_SUCCESS;
2851 priv->op_type = MODEST_MAIL_OPERATION_TYPE_INFO;
2855 /* This mail operation does nothing actually */
2856 modest_mail_operation_notify_start (self);
2857 modest_mail_operation_notify_end (self);