fc77575671ea512e9bfa795c4c8a992fd5671aef
[modest] / src / modest-tny-send-queue.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30
31 #include <modest-tny-send-queue.h>
32 #include <tny-simple-list.h>
33 #include <tny-iterator.h>
34 #include <tny-folder.h>
35 #include <tny-error.h>
36 #include <tny-camel-msg.h>
37 #include <tny-folder-change.h>
38 #include <tny-folder-observer.h>
39 #include <modest-tny-account.h>
40 #include <modest-runtime.h>
41 #include <modest-platform.h>
42 #include <widgets/modest-window-mgr.h>
43 #include <modest-marshal.h>
44 #include <modest-debug.h>
45 #include <string.h> /* strcmp */
46
47 /* 'private'/'protected' functions */
48 static void modest_tny_send_queue_class_init (ModestTnySendQueueClass *klass);
49 static void modest_tny_send_queue_finalize   (GObject *obj);
50 static void modest_tny_send_queue_instance_init (GTypeInstance *instance, gpointer g_class);
51
52 /* Signal handlers */ 
53 static void _on_msg_start_sending (TnySendQueue *self, 
54                                    TnyHeader *header, 
55                                    TnyMsg *msg, 
56                                    int done, 
57                                    int total, 
58                                    gpointer user_data);
59
60 static void _on_msg_has_been_sent (TnySendQueue *self, 
61                                    TnyHeader *header, 
62                                    TnyMsg *msg, 
63                                    int done, 
64                                    int total, 
65                                    gpointer user_data);
66
67 static void _on_msg_error_happened (TnySendQueue *self, 
68                                     TnyHeader *header, 
69                                     TnyMsg *msg, 
70                                     GError *err, 
71                                     gpointer user_data);
72
73 static void _on_queue_start        (TnySendQueue *self, 
74                                     gpointer user_data);
75
76 static void modest_tny_send_queue_add_async (TnySendQueue *self, 
77                                              TnyMsg *msg, 
78                                              TnySendQueueAddCallback callback, 
79                                              TnyStatusCallback status_callback, 
80                                              gpointer user_data);
81
82 static TnyFolder* modest_tny_send_queue_get_outbox  (TnySendQueue *self);
83 static TnyFolder* modest_tny_send_queue_get_sentbox (TnySendQueue *self);
84
85 /* list my signals  */
86 enum {
87         STATUS_CHANGED_SIGNAL,
88         LAST_SIGNAL
89 };
90
91 typedef struct _SendInfo SendInfo;
92 struct _SendInfo {
93         gchar* msg_id;
94         ModestTnySendQueueStatus status;
95 };
96
97 typedef struct _ModestTnySendQueuePrivate ModestTnySendQueuePrivate;
98 struct _ModestTnySendQueuePrivate {
99         /* Queued infos */
100         GQueue* queue;
101
102         /* The info that is currently being sent */
103         GList* current;
104
105         /* Special folders */
106         TnyFolder *outbox;
107         TnyFolder *sentbox;
108
109         /* last was send receive operation?*/
110         gboolean requested_send_receive;
111 };
112
113 #define MODEST_TNY_SEND_QUEUE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
114                                                    MODEST_TYPE_TNY_SEND_QUEUE, \
115                                                    ModestTnySendQueuePrivate))
116
117 /* globals */
118 static TnyCamelSendQueueClass *parent_class = NULL;
119
120 /* uncomment the following if you have defined any signals */
121 static guint signals[LAST_SIGNAL] = {0};
122
123 /*
124  * this thread actually tries to send all the mails in the outbox and keeps
125  * track of their state.
126  */
127
128 static int
129 on_modest_tny_send_queue_compare_id (gconstpointer info, gconstpointer msg_id)
130 {
131         g_return_val_if_fail (info && ((SendInfo*)info)->msg_id && msg_id, -1);
132         
133         return strcmp( ((SendInfo*)info)->msg_id, msg_id);
134 }
135
136 static void
137 modest_tny_send_queue_info_free (SendInfo *info)
138 {
139         g_free(info->msg_id);
140         g_slice_free(SendInfo, info);
141 }
142
143 static GList*
144 modest_tny_send_queue_lookup_info (ModestTnySendQueue *self, const gchar *msg_id)
145 {
146         ModestTnySendQueuePrivate *priv;
147         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
148         
149         return g_queue_find_custom (priv->queue, msg_id, on_modest_tny_send_queue_compare_id);
150 }
151
152
153 static void
154 queue_item_to_string (gpointer data, gchar **user_data)
155 {
156         SendInfo *info = (SendInfo*)data;
157         const gchar *status;
158         gchar *tmp;
159         
160         if (!(user_data && *user_data))
161                 return;
162         
163         switch (info->status) {
164         case MODEST_TNY_SEND_QUEUE_UNKNOWN: status = "UNKNOWN"; break;
165         case MODEST_TNY_SEND_QUEUE_WAITING: status = "WAITING"; break;
166         case MODEST_TNY_SEND_QUEUE_SUSPENDED: status = "SUSPENDED"; break;
167         case MODEST_TNY_SEND_QUEUE_SENDING: status = "SENDING"; break;
168         case MODEST_TNY_SEND_QUEUE_FAILED: status = "FAILED"; break;
169         default: status= "UNEXPECTED"; break;
170         }
171
172         tmp = g_strdup_printf ("%s\"%s\" => [%s]\n",
173                                *user_data, info->msg_id, status);
174         g_free (*user_data);
175         *user_data = tmp;
176 }
177
178 gchar*
179 modest_tny_send_queue_to_string (ModestTnySendQueue *self)
180 {
181         gchar *str;
182         ModestTnySendQueuePrivate *priv;
183
184         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), NULL);
185         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
186
187         str = g_strdup_printf ("items in the send queue: %d\n",
188                                g_queue_get_length (priv->queue));
189         
190         g_queue_foreach (priv->queue, (GFunc)queue_item_to_string, &str);
191
192         return str;
193 }
194
195 static void
196 _on_added_to_outbox (TnySendQueue *self, 
197                      gboolean cancelled, 
198                      TnyMsg *msg, 
199                      GError *err,
200                      gpointer user_data) 
201 {
202         ModestTnySendQueuePrivate *priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE(self);
203         TnyHeader *header = NULL;
204         SendInfo *info = NULL;
205         GList* existing = NULL;
206         gchar* msg_id = NULL;
207
208         g_return_if_fail (TNY_IS_SEND_QUEUE(self));
209         g_return_if_fail (TNY_IS_CAMEL_MSG(msg));
210
211         header = tny_msg_get_header (msg);
212         msg_id = modest_tny_send_queue_get_msg_id (header);
213         if (!msg_id) {
214                 g_warning ("%s: No msg_id returned for header", __FUNCTION__);
215                 goto end;
216         }
217
218         /* Put newly added message in WAITING state */
219         existing = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE(self), msg_id);
220         if(existing != NULL) {
221                 info = existing->data;
222                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
223         } else {
224                 info = g_slice_new (SendInfo);
225                 info->msg_id = msg_id;
226                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
227                 g_queue_push_tail (priv->queue, info);
228         }
229
230         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
231
232  end:
233         g_object_unref (G_OBJECT(header));
234 }
235
236 static void
237 _add_message (ModestTnySendQueue *self, TnyHeader *header)
238 {
239         ModestWindowMgr *mgr = NULL;
240         ModestTnySendQueuePrivate *priv;
241         SendInfo *info = NULL;
242         GList* existing = NULL;
243         gchar* msg_uid = NULL;
244         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
245         gboolean editing = FALSE;
246
247         g_return_if_fail (TNY_IS_SEND_QUEUE(self));
248         g_return_if_fail (TNY_IS_HEADER(header));
249         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
250         
251         /* Check whether the mail is already in the queue */
252         msg_uid = modest_tny_send_queue_get_msg_id (header);
253         status = modest_tny_send_queue_get_msg_status (self, msg_uid);
254         switch (status) {
255         case MODEST_TNY_SEND_QUEUE_UNKNOWN:
256         case MODEST_TNY_SEND_QUEUE_SUSPENDED:
257         case MODEST_TNY_SEND_QUEUE_FAILED:
258
259                 /* Check if it already exists on queue */
260                 existing = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE(self), msg_uid);
261                 if(existing != NULL)
262                         break;
263                 
264                 /* Check if its being edited */
265                 mgr = modest_runtime_get_window_mgr ();
266                 editing = modest_window_mgr_find_registered_header (mgr, header, NULL);
267                 if (editing)
268                         break;
269                 
270                 /* Add new meesage info */
271                 info = g_slice_new (SendInfo);
272                 info->msg_id = strdup(msg_uid);
273                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
274                 g_queue_push_tail (priv->queue, info);
275                 break;
276         default:
277                 break;
278         }
279
280         /* Free */
281         g_free(msg_uid);
282 }
283
284 static void 
285 modest_tny_send_queue_add_async (TnySendQueue *self, 
286                                  TnyMsg *msg, 
287                                  TnySendQueueAddCallback callback, 
288                                  TnyStatusCallback status_callback, 
289                                  gpointer user_data)
290 {
291         /* Call the superclass passing our own callback */
292         TNY_CAMEL_SEND_QUEUE_CLASS(parent_class)->add_async (self, msg, _on_added_to_outbox, NULL, NULL);
293 }
294
295
296 static TnyFolder*
297 modest_tny_send_queue_get_sentbox (TnySendQueue *self)
298 {
299         ModestTnySendQueuePrivate *priv;
300
301         g_return_val_if_fail (self, NULL);
302
303         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
304
305         return g_object_ref (priv->sentbox);
306 }
307
308
309 static TnyFolder*
310 modest_tny_send_queue_get_outbox (TnySendQueue *self)
311 {
312         ModestTnySendQueuePrivate *priv;
313
314         g_return_val_if_fail (self, NULL);
315
316         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
317
318         return g_object_ref (priv->outbox);
319 }
320
321 GType
322 modest_tny_send_queue_get_type (void)
323 {
324         static GType my_type = 0;
325
326         if (my_type == 0) {
327                 static const GTypeInfo my_info = {
328                         sizeof(ModestTnySendQueueClass),
329                         NULL,           /* base init */
330                         NULL,           /* base finalize */
331                         (GClassInitFunc) modest_tny_send_queue_class_init,
332                         NULL,           /* class finalize */
333                         NULL,           /* class data */
334                         sizeof(ModestTnySendQueue),
335                         0,              /* n_preallocs */
336                         (GInstanceInitFunc) modest_tny_send_queue_instance_init,
337                         NULL
338                 };
339                 
340                 my_type = g_type_register_static (TNY_TYPE_CAMEL_SEND_QUEUE,
341                                                   "ModestTnySendQueue",
342                                                   &my_info, 0);
343         }
344         return my_type;
345 }
346
347
348 static void
349 modest_tny_send_queue_class_init (ModestTnySendQueueClass *klass)
350 {
351         GObjectClass *gobject_class;
352
353         gobject_class = (GObjectClass*) klass;
354         
355         parent_class            = g_type_class_peek_parent (klass);
356         gobject_class->finalize = modest_tny_send_queue_finalize;
357
358         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->add_async   = modest_tny_send_queue_add_async;
359         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_outbox  = modest_tny_send_queue_get_outbox;
360         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_sentbox = modest_tny_send_queue_get_sentbox;
361         klass->status_changed   = NULL;
362
363         signals[STATUS_CHANGED_SIGNAL] =
364                 g_signal_new ("status_changed",
365                               G_TYPE_FROM_CLASS (gobject_class),
366                               G_SIGNAL_RUN_FIRST,
367                               G_STRUCT_OFFSET (ModestTnySendQueueClass, status_changed),
368                               NULL, NULL,
369                               modest_marshal_VOID__STRING_INT,
370                               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
371
372         g_type_class_add_private (gobject_class, sizeof(ModestTnySendQueuePrivate));
373 }
374
375 static void
376 modest_tny_send_queue_instance_init (GTypeInstance *instance, gpointer g_class)
377 {
378         ModestTnySendQueuePrivate *priv;
379
380         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (instance);
381         priv->queue = g_queue_new();
382         priv->current = NULL;
383 }
384
385 static void
386 modest_tny_send_queue_finalize (GObject *obj)
387 {
388         ModestTnySendQueuePrivate *priv;
389                 
390         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (obj);
391
392         g_queue_foreach (priv->queue, (GFunc)modest_tny_send_queue_info_free, NULL);
393         g_queue_free (priv->queue);
394
395         g_object_unref (priv->outbox);
396         g_object_unref (priv->sentbox);
397
398         G_OBJECT_CLASS(parent_class)->finalize (obj);
399 }
400
401 ModestTnySendQueue*
402 modest_tny_send_queue_new (TnyCamelTransportAccount *account)
403 {
404         ModestTnySendQueue *self = NULL;
405         ModestTnySendQueuePrivate *priv = NULL;
406         TnyIterator *iter = NULL;
407         TnyList *headers = NULL;
408         
409         g_return_val_if_fail (TNY_IS_CAMEL_TRANSPORT_ACCOUNT(account), NULL);
410         
411         self = MODEST_TNY_SEND_QUEUE(g_object_new(MODEST_TYPE_TNY_SEND_QUEUE, NULL));
412         
413         /* Connect signals to control when a msg is being or has been sent */
414         g_signal_connect (G_OBJECT(self), "msg-sending",
415                           G_CALLBACK(_on_msg_start_sending),
416                           NULL);                          
417         g_signal_connect (G_OBJECT(self), "msg-sent",
418                           G_CALLBACK(_on_msg_has_been_sent), 
419                           NULL);
420         g_signal_connect (G_OBJECT(self), "error-happened",
421                           G_CALLBACK(_on_msg_error_happened),
422                           NULL);
423         g_signal_connect (G_OBJECT (self), "queue-start",
424                           G_CALLBACK (_on_queue_start),
425                           NULL);
426
427         /* Set outbox and sentbox */
428         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
429         priv->outbox  = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
430                                                                TNY_FOLDER_TYPE_OUTBOX);
431         priv->sentbox = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
432                                                                TNY_FOLDER_TYPE_SENT);
433
434         priv->requested_send_receive = FALSE;
435
436
437         headers = tny_simple_list_new ();       
438         tny_folder_get_headers (priv->outbox, headers, TRUE, NULL);
439
440         /* Add messages to our internal queue */
441         iter = tny_list_create_iterator (headers);
442         while (!tny_iterator_is_done (iter)) {
443                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
444                 _add_message (self, header); 
445                 g_object_unref (header);                
446
447                 tny_iterator_next (iter);
448         }
449
450         /* Reenable suspended items */
451         modest_tny_send_queue_wakeup (self);
452
453         /* Frees */
454         g_object_unref (headers);
455         g_object_unref (iter);
456
457         /* Do this at the end, because it'll call tny_send_queue_flush
458            which will call tny_send_queue_get_outbox and
459            tny_send_queue_get_sentbox */
460         tny_camel_send_queue_set_transport_account (TNY_CAMEL_SEND_QUEUE(self),
461                                                     account); 
462
463         return self;
464 }
465
466 gboolean
467 modest_tny_send_queue_msg_is_being_sent (ModestTnySendQueue* self,
468                                          const gchar *msg_id)
469 {       
470         ModestTnySendQueueStatus status;
471         
472         g_return_val_if_fail (msg_id != NULL, FALSE); 
473         
474         status = modest_tny_send_queue_get_msg_status (self, msg_id);
475         return status == MODEST_TNY_SEND_QUEUE_SENDING;
476 }
477
478 gboolean
479 modest_tny_send_queue_sending_in_progress (ModestTnySendQueue* self)
480 {       
481         ModestTnySendQueuePrivate *priv;
482
483         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), FALSE);
484         
485         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
486         
487         return priv->current != NULL;
488 }
489
490 ModestTnySendQueueStatus
491 modest_tny_send_queue_get_msg_status (ModestTnySendQueue *self, const gchar *msg_id)
492 {
493         GList *item;
494
495         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), MODEST_TNY_SEND_QUEUE_UNKNOWN);
496         g_return_val_if_fail (msg_id, MODEST_TNY_SEND_QUEUE_UNKNOWN);
497
498         item = modest_tny_send_queue_lookup_info (self, msg_id);
499         if (!item)
500                 return MODEST_TNY_SEND_QUEUE_UNKNOWN;
501         else
502                 return ((SendInfo*)item->data)->status;
503 }
504
505 gchar *
506 modest_tny_send_queue_get_msg_id (TnyHeader *header)
507 {
508         gchar* msg_uid = NULL;
509         const gchar *subject;
510         time_t date_received;
511                 
512         g_return_val_if_fail (header && TNY_IS_HEADER(header), NULL);
513
514         /* Get message uid */
515         subject = tny_header_get_subject (header);
516         date_received = tny_header_get_date_received (header);
517
518         msg_uid = g_strdup_printf ("%s %d", subject, (int) date_received);
519
520         return msg_uid;
521 }
522
523
524 static void
525 _on_msg_start_sending (TnySendQueue *self, TnyHeader *header,
526                        TnyMsg *msg, int done, int total, gpointer user_data)
527 {
528         ModestTnySendQueuePrivate *priv = NULL;
529         GList *item = NULL;
530         SendInfo *info = NULL;
531         gchar *msg_id = NULL;
532
533         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
534         
535         /* Get message uid */
536         msg_id = modest_tny_send_queue_get_msg_id (header);
537         if (msg_id) 
538                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
539         else
540                 g_warning ("%s: could not get msg-id for header", __FUNCTION__);
541         
542         if (item) {
543                 /* Set current status item */
544                 info = item->data;
545                 info->status = MODEST_TNY_SEND_QUEUE_SENDING;
546                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
547                 priv->current = item;
548         } else
549                 g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_id);
550         
551         /* free */
552         g_free (msg_id);
553 }
554
555 static void 
556 _on_msg_has_been_sent (TnySendQueue *self,
557                        TnyHeader *header,
558                        TnyMsg *msg, 
559                        int done, 
560                        int total,
561                        gpointer user_data)
562 {
563         ModestTnySendQueuePrivate *priv;
564         gchar *msg_id = NULL;
565         GList *item;
566
567         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
568
569         /* Get message uid */
570         msg_id = modest_tny_send_queue_get_msg_id (header);
571
572         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
573
574         /* Get status info */
575         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
576
577
578         /* TODO: note that item=NULL must not happen, but I found that
579            tinymail is issuing the message-sent signal twice, because
580            tny_camel_send_queue_update is called twice for each
581            message sent. This must be fixed in tinymail. Sergio */
582         if (item) {
583                 /* Remove status info */
584                 modest_tny_send_queue_info_free (item->data);
585                 g_queue_delete_link (priv->queue, item);
586                 priv->current = NULL;
587                 
588                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_sent"));
589         }
590
591         /* free */
592         g_free(msg_id);
593 }
594
595 static void 
596 _on_msg_error_happened (TnySendQueue *self,
597                         TnyHeader *header,
598                         TnyMsg *msg,
599                         GError *err,
600                         gpointer user_data)
601 {
602         ModestTnySendQueuePrivate *priv = NULL;
603         
604         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
605
606         /* Note that header could be NULL. Tinymail notifies about
607            generic send queue errors with this signal as well, and
608            those notifications are not bound to any particular header
609            or message */
610         if (header && TNY_IS_HEADER (header)) {
611                 SendInfo *info = NULL;
612                 GList *item = NULL;
613                 gchar* msg_uid = NULL;
614
615                 /* Get sending info (create new if it doesn not exist) */
616                 msg_uid = modest_tny_send_queue_get_msg_id (header);
617                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
618                                                           msg_uid);
619
620                 /* TODO: this should not happen (but it does), so the
621                    problem should be located in the way we generate
622                    the message uids */
623                 if (!item) {
624                         g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_uid);
625                         g_free(msg_uid);
626                         return;
627                 }
628                 
629                 info = item->data;
630
631                 /* Keep in queue so that we remember that the opertion has failed */
632                 /* and was not just cancelled */
633                 if (err->code == TNY_SYSTEM_ERROR_CANCEL)
634                         info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
635                 else
636                         info->status = MODEST_TNY_SEND_QUEUE_FAILED;
637                 priv->current = NULL;
638                 
639                 /* Notify status has changed */
640                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
641
642                 /* free */
643                 g_free(msg_uid);
644         }
645 }
646
647 static void 
648 _on_queue_start (TnySendQueue *self,
649                  gpointer data)
650 {
651         ModestMailOperation *mail_op;
652
653         mail_op = modest_mail_operation_new (NULL);
654         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
655                                          mail_op);
656         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
657         g_object_unref (mail_op);
658 }
659
660 static void 
661 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
662 {
663         GSList **send_queues = (GSList **) userdata;
664         *send_queues = g_slist_prepend (*send_queues, value);
665 }
666
667 /* This function shouldn't be here. Move it to another place. Sergio */
668 ModestTnySendQueueStatus
669 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
670 {
671         ModestCacheMgr *cache_mgr = NULL;
672         GHashTable     *send_queue_cache = NULL;
673         ModestTnyAccountStore *accounts_store = NULL;
674         TnyList *accounts = NULL;
675         TnyIterator *iter = NULL;
676         TnyTransportAccount *account = NULL;
677         GSList *send_queues = NULL, *node;
678         /* get_msg_status returns suspended by default, so we want to detect changes */
679         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
680         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
681         gchar *msg_uid = NULL;
682         ModestTnySendQueue *send_queue = NULL;
683         
684         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
685
686         msg_uid = modest_tny_send_queue_get_msg_id (header);
687         cache_mgr = modest_runtime_get_cache_mgr ();
688         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
689                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
690         
691         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
692         if (send_queues == NULL) {
693                 accounts = tny_simple_list_new (); 
694                 accounts_store = modest_runtime_get_account_store ();
695                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
696                                                 accounts, 
697                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
698                 
699                 iter = tny_list_create_iterator (accounts);
700                 while (!tny_iterator_is_done (iter)) {                  
701                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
702                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
703                         g_object_unref(account);
704
705                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
706                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
707                                 status = queue_status;
708                                 break;
709                         }
710                         tny_iterator_next (iter);
711                 }
712                 g_object_unref (iter);
713                 g_object_unref (accounts);
714         }
715         else {
716                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
717                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
718                         
719                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
720                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
721                                 status = queue_status;
722                                 break;
723                         }
724                 }
725         }
726
727         g_free(msg_uid);
728         g_slist_free (send_queues);
729         return status;
730 }
731
732 void   
733 modest_tny_send_queue_wakeup (ModestTnySendQueue *self)
734 {
735         ModestTnySendQueuePrivate *priv;
736         TnyList *headers;
737         TnyIterator *iter;
738
739         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
740
741         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
742
743         headers = tny_simple_list_new ();
744         tny_folder_get_headers (priv->outbox, headers, TRUE, NULL);
745
746         /* Wake up every single suspended header */
747         iter = tny_list_create_iterator (headers);
748         while (!tny_iterator_is_done (iter)) {
749                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
750
751                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
752                         gchar *msg_id;
753                         GList *item;
754                         SendInfo *info;
755
756                         /* Unset the suspended flag */
757                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
758
759                         /* Notify view */
760                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
761                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
762                         info = (SendInfo *) item->data;
763                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
764                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
765                         
766                         /* Frees */
767                         g_free (msg_id);
768                 }
769
770                 /* Frees */
771                 g_object_unref (header);
772                 tny_iterator_next (iter);
773         }
774
775         /* Make changes persistent on disk */
776         tny_folder_sync_async (priv->outbox, FALSE, NULL, NULL, NULL);
777
778         /* Frees */
779         g_object_unref (iter);
780         g_object_unref (G_OBJECT (headers));
781 }
782
783 gboolean 
784 modest_tny_send_queue_get_requested_send_receive (ModestTnySendQueue *self)
785 {
786         ModestTnySendQueuePrivate *priv;
787
788         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE (self), FALSE);
789         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
790
791         return priv->requested_send_receive;
792 }
793
794 void 
795 modest_tny_send_queue_set_requested_send_receive (ModestTnySendQueue *self, gboolean requested_send_receive)
796 {
797         ModestTnySendQueuePrivate *priv;
798
799         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
800         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
801
802         priv->requested_send_receive = requested_send_receive;
803 }