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