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