e0716b9977bbeba6ce63e379168b6fa1de7c6e51
[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         SendInfo *info = NULL;
595         GList *item = NULL;
596         gchar* msg_uid = NULL;
597         
598         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
599         
600         /* Get sending info (create new if it doesn not exist) */
601         msg_uid = modest_tny_send_queue_get_msg_id (header);
602         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
603                                                   msg_uid);     
604
605         info = item->data;
606
607         /* Keep in queue so that we remember that the opertion has failed */
608         /* and was not just cancelled */
609         if (err->code == TNY_SYSTEM_ERROR_CANCEL)
610                 info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
611         else
612                 info->status = MODEST_TNY_SEND_QUEUE_FAILED;
613         priv->current = NULL;
614         
615         /* Notify status has changed */
616         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
617
618         /* free */
619         g_free(msg_uid);
620 }
621
622 static void 
623 _on_queue_start (TnySendQueue *self,
624                  gpointer data)
625 {
626         ModestMailOperation *mail_op;
627
628         mail_op = modest_mail_operation_new (NULL);
629         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
630                                          mail_op);
631         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
632         g_object_unref (mail_op);
633 }
634
635 static void 
636 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
637 {
638         GSList **send_queues = (GSList **) userdata;
639         *send_queues = g_slist_prepend (*send_queues, value);
640 }
641
642 /* This function shouldn't be here. Move it to another place. Sergio */
643 ModestTnySendQueueStatus
644 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
645 {
646         ModestCacheMgr *cache_mgr = NULL;
647         GHashTable     *send_queue_cache = NULL;
648         ModestTnyAccountStore *accounts_store = NULL;
649         TnyList *accounts = NULL;
650         TnyIterator *iter = NULL;
651         TnyTransportAccount *account = NULL;
652         GSList *send_queues = NULL, *node;
653         /* get_msg_status returns suspended by default, so we want to detect changes */
654         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
655         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
656         gchar *msg_uid = NULL;
657         ModestTnySendQueue *send_queue = NULL;
658         
659         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
660
661         msg_uid = modest_tny_send_queue_get_msg_id (header);
662         cache_mgr = modest_runtime_get_cache_mgr ();
663         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
664                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
665         
666         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
667         if (send_queues == NULL) {
668                 accounts = tny_simple_list_new (); 
669                 accounts_store = modest_runtime_get_account_store ();
670                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
671                                                 accounts, 
672                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
673                 
674                 iter = tny_list_create_iterator (accounts);
675                 while (!tny_iterator_is_done (iter)) {                  
676                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
677                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
678                         g_object_unref(account);
679
680                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
681                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
682                                 status = queue_status;
683                                 break;
684                         }
685                         tny_iterator_next (iter);
686                 }
687                 g_object_unref (iter);
688                 g_object_unref (accounts);
689         }
690         else {
691                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
692                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
693                         
694                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
695                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
696                                 status = queue_status;
697                                 break;
698                         }
699                 }
700         }
701
702         g_free(msg_uid);
703         g_slist_free (send_queues);
704         return status;
705 }
706
707 void   
708 modest_tny_send_queue_wakeup (ModestTnySendQueue *self)
709 {
710         ModestTnySendQueuePrivate *priv;
711         TnyList *headers;
712         TnyIterator *iter;
713
714         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
715
716         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
717
718         headers = tny_simple_list_new ();
719         tny_folder_get_headers (priv->outbox, headers, TRUE, NULL);
720
721         /* Wake up every single suspended header */
722         iter = tny_list_create_iterator (headers);
723         while (!tny_iterator_is_done (iter)) {
724                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
725
726                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
727                         gchar *msg_id;
728                         GList *item;
729                         SendInfo *info;
730
731                         /* Unset the suspended flag */
732                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
733
734                         /* Notify view */
735                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
736                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
737                         info = (SendInfo *) item->data;
738                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
739                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
740                         
741                         /* Frees */
742                         g_free (msg_id);
743                 }
744
745                 /* Frees */
746                 g_object_unref (header);
747                 tny_iterator_next (iter);
748         }
749
750         /* Make changes persistent on disk */
751         tny_folder_sync_async (priv->outbox, FALSE, NULL, NULL, NULL);
752
753         /* Frees */
754         g_object_unref (iter);
755         g_object_unref (G_OBJECT (headers));
756 }