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