b1f8560eb00e31bc843a9627dd4eb0bfdf48d7a8
[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 (TnyCamelSendQueue *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 (TnyCamelSendQueue *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_func (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_func  = modest_tny_send_queue_add_async;
352         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_outbox_func  = modest_tny_send_queue_get_outbox;
353         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_sentbox_func = 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 (G_OBJECT (headers));
446         g_object_unref (G_OBJECT (priv->outbox));
447         g_object_unref (iter);
448
449         /* Do this at the end, because it'll call tny_send_queue_flush
450            which will call tny_send_queue_get_outbox and
451            tny_send_queue_get_sentbox */
452         tny_camel_send_queue_set_transport_account (TNY_CAMEL_SEND_QUEUE(self),
453                                                     account); 
454
455         return self;
456 }
457
458 gboolean
459 modest_tny_send_queue_msg_is_being_sent (ModestTnySendQueue* self,
460                                          const gchar *msg_id)
461 {       
462         ModestTnySendQueueStatus status;
463         
464         g_return_val_if_fail (msg_id != NULL, FALSE); 
465         
466         status = modest_tny_send_queue_get_msg_status (self, msg_id);
467         return status == MODEST_TNY_SEND_QUEUE_SENDING;
468 }
469
470 gboolean
471 modest_tny_send_queue_sending_in_progress (ModestTnySendQueue* self)
472 {       
473         ModestTnySendQueuePrivate *priv;
474
475         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), FALSE);
476         
477         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
478         
479         return priv->current != NULL;
480 }
481
482 ModestTnySendQueueStatus
483 modest_tny_send_queue_get_msg_status (ModestTnySendQueue *self, const gchar *msg_id)
484 {
485         GList *item;
486
487         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), MODEST_TNY_SEND_QUEUE_UNKNOWN);
488         g_return_val_if_fail (msg_id, MODEST_TNY_SEND_QUEUE_UNKNOWN);
489
490         item = modest_tny_send_queue_lookup_info (self, msg_id);
491         if (!item)
492                 return MODEST_TNY_SEND_QUEUE_UNKNOWN;
493         else
494                 return ((SendInfo*)item->data)->status;
495 }
496
497 gchar *
498 modest_tny_send_queue_get_msg_id (TnyHeader *header)
499 {
500         gchar* msg_uid = NULL;
501         const gchar *subject;
502         time_t date_received;
503                 
504         g_return_val_if_fail (header && TNY_IS_HEADER(header), NULL);
505
506         /* Get message uid */
507         subject = tny_header_get_subject (header);
508         date_received = tny_header_get_date_received (header);
509
510         msg_uid = g_strdup_printf ("%s %d", subject, (int) date_received);
511
512         return msg_uid;
513 }
514
515
516 static void
517 _on_msg_start_sending (TnySendQueue *self,
518                        TnyHeader *header,
519                        TnyMsg *msg,
520                        int done, 
521                        int total,
522                        gpointer user_data)
523 {
524         ModestTnySendQueuePrivate *priv = NULL;
525         GList *item = NULL;
526         SendInfo *info = NULL;
527         gchar *msg_id = NULL;
528
529         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
530         
531         /* Get message uid */
532         msg_id = modest_tny_send_queue_get_msg_id (header);
533
534         /* Get status info */
535         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
536
537         /* Set current status item */
538         info = item->data;
539         info->status = MODEST_TNY_SEND_QUEUE_SENDING;
540         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
541         priv->current = item;
542
543         /* free */
544         g_free (msg_id);
545 }
546
547 static void 
548 _on_msg_has_been_sent (TnySendQueue *self,
549                        TnyHeader *header,
550                        TnyMsg *msg, 
551                        int done, 
552                        int total,
553                        gpointer user_data)
554 {
555         ModestTnySendQueuePrivate *priv;
556         gchar *msg_id = NULL;
557         GList *item;
558
559         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
560
561         /* Get message uid */
562         msg_id = modest_tny_send_queue_get_msg_id (header);
563
564         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
565
566         /* Get status info */
567         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
568
569
570         /* TODO: note that item=NULL must not happen, but I found that
571            tinymail is issuing the message-sent signal twice, because
572            tny_camel_send_queue_update is called twice for each
573            message sent. This must be fixed in tinymail. Sergio */
574         if (item) {
575                 /* Remove status info */
576                 modest_tny_send_queue_info_free (item->data);
577                 g_queue_delete_link (priv->queue, item);
578                 priv->current = NULL;
579                 
580                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_sent"));
581         }
582
583         /* free */
584         g_free(msg_id);
585 }
586
587 static void 
588 _on_msg_error_happened (TnySendQueue *self,
589                         TnyHeader *header,
590                         TnyMsg *msg,
591                         GError *err,
592                         gpointer user_data)
593 {
594         ModestTnySendQueuePrivate *priv = NULL;
595         SendInfo *info = NULL;
596         GList *item = NULL;
597         gchar* msg_uid = NULL;
598         
599         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
600         
601         /* Get sending info (create new if it doesn not exist) */
602         msg_uid = modest_tny_send_queue_get_msg_id (header);
603         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
604                                                   msg_uid);     
605
606         info = item->data;
607
608         /* Keep in queue so that we remember that the opertion has failed */
609         /* and was not just cancelled */
610         if (err->code == TNY_SYSTEM_ERROR_CANCEL)
611                 info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
612         else
613                 info->status = MODEST_TNY_SEND_QUEUE_FAILED;
614         priv->current = NULL;
615         
616         /* Notify status has changed */
617         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
618
619         /* free */
620         g_free(msg_uid);
621 }
622
623 static void 
624 _on_queue_start (TnySendQueue *self,
625                  gpointer data)
626 {
627         ModestMailOperation *mail_op;
628
629         mail_op = modest_mail_operation_new (NULL);
630         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
631                                          mail_op);
632         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
633         g_object_unref (mail_op);
634 }
635
636 static void 
637 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
638 {
639         GSList **send_queues = (GSList **) userdata;
640         *send_queues = g_slist_prepend (*send_queues, value);
641 }
642
643 /* This function shouldn't be here. Move it to another place. Sergio */
644 ModestTnySendQueueStatus
645 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
646 {
647         ModestCacheMgr *cache_mgr = NULL;
648         GHashTable     *send_queue_cache = NULL;
649         ModestTnyAccountStore *accounts_store = NULL;
650         TnyList *accounts = NULL;
651         TnyIterator *iter = NULL;
652         TnyTransportAccount *account = NULL;
653         GSList *send_queues = NULL, *node;
654         /* get_msg_status returns suspended by default, so we want to detect changes */
655         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
656         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
657         gchar *msg_uid = NULL;
658         ModestTnySendQueue *send_queue = NULL;
659         
660         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
661
662         msg_uid = modest_tny_send_queue_get_msg_id (header);
663         cache_mgr = modest_runtime_get_cache_mgr ();
664         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
665                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
666         
667         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
668         if (send_queues == NULL) {
669                 accounts = tny_simple_list_new (); 
670                 accounts_store = modest_runtime_get_account_store ();
671                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
672                                                 accounts, 
673                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
674                 
675                 iter = tny_list_create_iterator (accounts);
676                 while (!tny_iterator_is_done (iter)) {                  
677                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
678                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
679                         g_object_unref(account);
680
681                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
682                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
683                                 status = queue_status;
684                                 break;
685                         }
686                         tny_iterator_next (iter);
687                 }
688                 g_object_unref (iter);
689                 g_object_unref (accounts);
690         }
691         else {
692                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
693                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
694                         
695                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
696                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
697                                 status = queue_status;
698                                 break;
699                         }
700                 }
701         }
702
703         g_free(msg_uid);
704         g_slist_free (send_queues);
705         return status;
706 }
707
708 void   
709 modest_tny_send_queue_wakeup (ModestTnySendQueue *self)
710 {
711         ModestTnySendQueuePrivate *priv;
712         TnyList *headers;
713         TnyIterator *iter;
714
715         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
716
717         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
718
719         headers = tny_simple_list_new ();
720         tny_folder_get_headers (priv->outbox, headers, TRUE, NULL);
721
722         /* Wake up every single suspended header */
723         iter = tny_list_create_iterator (headers);
724         while (!tny_iterator_is_done (iter)) {
725                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
726
727                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
728                         gchar *msg_id;
729                         GList *item;
730                         SendInfo *info;
731
732                         /* Unset the suspended flag */
733                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
734
735                         /* Notify view */
736                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
737                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
738                         info = (SendInfo *) item->data;
739                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
740                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
741                         
742                         /* Frees */
743                         g_free (msg_id);
744                 }
745
746                 /* Frees */
747                 g_object_unref (header);
748                 tny_iterator_next (iter);
749         }
750
751         /* Frees */
752         g_object_unref (iter);
753         g_object_unref (G_OBJECT (headers));
754 }