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