Use always the #define to refer to the signature separator
[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 _on_queue_stop         (TnySendQueue *self,
77                                     gpointer data);
78
79 static void modest_tny_send_queue_add_async (TnySendQueue *self, 
80                                              TnyMsg *msg, 
81                                              TnySendQueueAddCallback callback, 
82                                              TnyStatusCallback status_callback, 
83                                              gpointer user_data);
84
85 static TnyFolder* modest_tny_send_queue_get_outbox  (TnySendQueue *self);
86 static TnyFolder* modest_tny_send_queue_get_sentbox (TnySendQueue *self);
87
88 /* list my signals  */
89 enum {
90         STATUS_CHANGED_SIGNAL,
91         LAST_SIGNAL
92 };
93
94 typedef struct _SendInfo SendInfo;
95 struct _SendInfo {
96         gchar* msg_id;
97         ModestTnySendQueueStatus status;
98 };
99
100 typedef struct _ModestTnySendQueuePrivate ModestTnySendQueuePrivate;
101 struct _ModestTnySendQueuePrivate {
102         /* Queued infos */
103         GQueue* queue;
104
105         /* The info that is currently being sent */
106         GList* current;
107
108         /* Special folders */
109         TnyFolder *outbox;
110         TnyFolder *sentbox;
111
112         /* last was send receive operation?*/
113         gboolean requested_send_receive;
114
115         gboolean sending;
116
117         GSList *sighandlers;
118 };
119
120 #define MODEST_TNY_SEND_QUEUE_GET_PRIVATE(o)      (G_TYPE_INSTANCE_GET_PRIVATE((o), \
121                                                    MODEST_TYPE_TNY_SEND_QUEUE, \
122                                                    ModestTnySendQueuePrivate))
123
124 /* globals */
125 static TnyCamelSendQueueClass *parent_class = NULL;
126
127 /* uncomment the following if you have defined any signals */
128 static guint signals[LAST_SIGNAL] = {0};
129
130 /*
131  * this thread actually tries to send all the mails in the outbox and keeps
132  * track of their state.
133  */
134
135 static int
136 on_modest_tny_send_queue_compare_id (gconstpointer info, gconstpointer msg_id)
137 {
138         g_return_val_if_fail (info && ((SendInfo*)info)->msg_id && msg_id, -1);
139         
140         return strcmp( ((SendInfo*)info)->msg_id, msg_id);
141 }
142
143 static void
144 modest_tny_send_queue_info_free (SendInfo *info)
145 {
146         g_free(info->msg_id);
147         g_slice_free(SendInfo, info);
148 }
149
150 static GList*
151 modest_tny_send_queue_lookup_info (ModestTnySendQueue *self, const gchar *msg_id)
152 {
153         ModestTnySendQueuePrivate *priv;
154         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
155         
156         return g_queue_find_custom (priv->queue, msg_id, on_modest_tny_send_queue_compare_id);
157 }
158
159
160 static void
161 queue_item_to_string (gpointer data, gchar **user_data)
162 {
163         SendInfo *info = (SendInfo*)data;
164         const gchar *status;
165         gchar *tmp;
166         
167         if (!(user_data && *user_data))
168                 return;
169         
170         switch (info->status) {
171         case MODEST_TNY_SEND_QUEUE_UNKNOWN: status = "UNKNOWN"; break;
172         case MODEST_TNY_SEND_QUEUE_WAITING: status = "WAITING"; break;
173         case MODEST_TNY_SEND_QUEUE_SUSPENDED: status = "SUSPENDED"; break;
174         case MODEST_TNY_SEND_QUEUE_SENDING: status = "SENDING"; break;
175         case MODEST_TNY_SEND_QUEUE_FAILED: status = "FAILED"; break;
176         default: status= "UNEXPECTED"; break;
177         }
178
179         tmp = g_strdup_printf ("%s\"%s\" => [%s]\n",
180                                *user_data, info->msg_id, status);
181         g_free (*user_data);
182         *user_data = tmp;
183 }
184
185 gchar*
186 modest_tny_send_queue_to_string (ModestTnySendQueue *self)
187 {
188         gchar *str;
189         ModestTnySendQueuePrivate *priv;
190
191         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), NULL);
192         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
193
194         str = g_strdup_printf ("items in the send queue: %d\n",
195                                g_queue_get_length (priv->queue));
196         
197         g_queue_foreach (priv->queue, (GFunc)queue_item_to_string, &str);
198
199         return str;
200 }
201
202 typedef struct {
203         TnySendQueueAddCallback callback;
204         gpointer user_data;
205 } AddAsyncHelper;
206
207 static void
208 _on_added_to_outbox (TnySendQueue *self, 
209                      gboolean cancelled, 
210                      TnyMsg *msg, 
211                      GError *err,
212                      gpointer user_data) 
213 {
214         ModestTnySendQueuePrivate *priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE(self);
215         TnyHeader *header = NULL;
216         SendInfo *info = NULL;
217         GList* existing = NULL;
218         gchar* msg_id = NULL;
219         AddAsyncHelper *helper;
220
221         g_return_if_fail (TNY_IS_SEND_QUEUE(self));
222         g_return_if_fail (TNY_IS_CAMEL_MSG(msg));
223
224         header = tny_msg_get_header (msg);
225         msg_id = modest_tny_send_queue_get_msg_id (header);
226         if (!msg_id) {
227                 g_warning ("%s: No msg_id returned for header", __FUNCTION__);
228                 goto end;
229         }
230
231         /* Put newly added message in WAITING state */
232         existing = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE(self), msg_id);
233         if(existing != NULL) {
234                 info = existing->data;
235                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
236         } else {
237                 info = g_slice_new (SendInfo);
238                 info->msg_id = msg_id;
239                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
240                 g_queue_push_tail (priv->queue, info);
241         }
242
243         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
244
245  end:
246         g_object_unref (G_OBJECT(header));
247
248         /* Call the user callback */
249         helper = (AddAsyncHelper *) user_data;
250         if (helper->callback)
251                 helper->callback (self, cancelled, msg, err, helper->user_data);
252         g_slice_free (AddAsyncHelper, helper);
253 }
254
255 static void
256 _add_message (ModestTnySendQueue *self, TnyHeader *header)
257 {
258         ModestWindowMgr *mgr = NULL;
259         ModestTnySendQueuePrivate *priv;
260         SendInfo *info = NULL;
261         GList* existing = NULL;
262         gchar* msg_uid = NULL;
263         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
264         gboolean editing = FALSE;
265
266         g_return_if_fail (TNY_IS_SEND_QUEUE(self));
267         g_return_if_fail (TNY_IS_HEADER(header));
268         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
269         
270         /* Check whether the mail is already in the queue */
271         msg_uid = modest_tny_send_queue_get_msg_id (header);
272         status = modest_tny_send_queue_get_msg_status (self, msg_uid);
273         switch (status) {
274         case MODEST_TNY_SEND_QUEUE_UNKNOWN:
275         case MODEST_TNY_SEND_QUEUE_SUSPENDED:
276         case MODEST_TNY_SEND_QUEUE_FAILED:
277
278                 /* Check if it already exists on queue */
279                 existing = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE(self), msg_uid);
280                 if(existing != NULL)
281                         break;
282                 
283                 /* Check if its being edited */
284                 mgr = modest_runtime_get_window_mgr ();
285                 editing = modest_window_mgr_find_registered_header (mgr, header, NULL);
286                 if (editing)
287                         break;
288                 
289                 /* Add new meesage info */
290                 info = g_slice_new0 (SendInfo);
291                 info->msg_id = strdup(msg_uid);
292                 info->status = MODEST_TNY_SEND_QUEUE_WAITING;
293                 g_queue_push_tail (priv->queue, info);
294                 break;
295         default:
296                 break;
297         }
298
299         /* Free */
300         g_free(msg_uid);
301 }
302
303 static void 
304 modest_tny_send_queue_add_async (TnySendQueue *self, 
305                                  TnyMsg *msg, 
306                                  TnySendQueueAddCallback callback, 
307                                  TnyStatusCallback status_callback, 
308                                  gpointer user_data)
309 {
310         AddAsyncHelper *helper = g_slice_new0 (AddAsyncHelper);
311         helper->callback = callback;
312         helper->user_data = user_data;
313
314         /* Call the superclass passing our own callback */
315         TNY_CAMEL_SEND_QUEUE_CLASS(parent_class)->add_async (self, msg, 
316                                                              _on_added_to_outbox, 
317                                                              status_callback, 
318                                                              helper);
319 }
320
321
322 static TnyFolder*
323 modest_tny_send_queue_get_sentbox (TnySendQueue *self)
324 {
325         ModestTnySendQueuePrivate *priv;
326
327         g_return_val_if_fail (self, NULL);
328
329         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
330
331         return g_object_ref (priv->sentbox);
332 }
333
334
335 static TnyFolder*
336 modest_tny_send_queue_get_outbox (TnySendQueue *self)
337 {
338         ModestTnySendQueuePrivate *priv;
339
340         g_return_val_if_fail (self, NULL);
341
342         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
343
344         return g_object_ref (priv->outbox);
345 }
346
347 GType
348 modest_tny_send_queue_get_type (void)
349 {
350         static GType my_type = 0;
351
352         if (my_type == 0) {
353                 static const GTypeInfo my_info = {
354                         sizeof(ModestTnySendQueueClass),
355                         NULL,           /* base init */
356                         NULL,           /* base finalize */
357                         (GClassInitFunc) modest_tny_send_queue_class_init,
358                         NULL,           /* class finalize */
359                         NULL,           /* class data */
360                         sizeof(ModestTnySendQueue),
361                         0,              /* n_preallocs */
362                         (GInstanceInitFunc) modest_tny_send_queue_instance_init,
363                         NULL
364                 };
365                 
366                 my_type = g_type_register_static (TNY_TYPE_CAMEL_SEND_QUEUE,
367                                                   "ModestTnySendQueue",
368                                                   &my_info, 0);
369         }
370         return my_type;
371 }
372
373
374 static void
375 modest_tny_send_queue_class_init (ModestTnySendQueueClass *klass)
376 {
377         GObjectClass *gobject_class;
378
379         gobject_class = (GObjectClass*) klass;
380         
381         parent_class            = g_type_class_peek_parent (klass);
382         gobject_class->finalize = modest_tny_send_queue_finalize;
383
384         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->add_async   = modest_tny_send_queue_add_async;
385         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_outbox  = modest_tny_send_queue_get_outbox;
386         TNY_CAMEL_SEND_QUEUE_CLASS(klass)->get_sentbox = modest_tny_send_queue_get_sentbox;
387         klass->status_changed   = NULL;
388
389         signals[STATUS_CHANGED_SIGNAL] =
390                 g_signal_new ("status_changed",
391                               G_TYPE_FROM_CLASS (gobject_class),
392                               G_SIGNAL_RUN_FIRST,
393                               G_STRUCT_OFFSET (ModestTnySendQueueClass, status_changed),
394                               NULL, NULL,
395                               modest_marshal_VOID__STRING_INT,
396                               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
397
398         g_type_class_add_private (gobject_class, sizeof(ModestTnySendQueuePrivate));
399 }
400
401 static void
402 modest_tny_send_queue_instance_init (GTypeInstance *instance, gpointer g_class)
403 {
404         ModestTnySendQueuePrivate *priv;
405
406         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (instance);
407         priv->queue = g_queue_new();
408         priv->current = NULL;
409         priv->outbox = NULL;
410         priv->sentbox = NULL;
411         priv->sending = FALSE;
412         priv->sighandlers = NULL;
413 }
414
415 static void
416 modest_tny_send_queue_finalize (GObject *obj)
417 {
418         ModestTnySendQueuePrivate *priv;
419
420         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (obj);
421
422         modest_signal_mgr_disconnect_all_and_destroy (priv->sighandlers);
423         priv->sighandlers = NULL;
424
425         g_queue_foreach (priv->queue, (GFunc)modest_tny_send_queue_info_free, NULL);
426         g_queue_free (priv->queue);
427
428         G_OBJECT_CLASS(parent_class)->finalize (obj);
429         g_object_unref (priv->outbox);
430         g_object_unref (priv->sentbox);
431 }
432
433 typedef struct {
434         TnyCamelTransportAccount *account;
435         ModestTnySendQueue *queue;
436 } GetHeadersInfo;
437
438 static void
439 new_queue_get_headers_async_cb (TnyFolder *folder, 
440                                 gboolean cancelled, 
441                                 TnyList *headers, 
442                                 GError *err, 
443                                 gpointer user_data)
444 {
445         ModestTnySendQueue *self;
446         TnyIterator *iter;
447         GetHeadersInfo *info;
448         ModestMailOperation *wakeup_op;
449
450         info = (GetHeadersInfo *) user_data;
451         self = MODEST_TNY_SEND_QUEUE (info->queue);
452
453         /* In case of error set the transport account anyway */
454         if (cancelled || err)
455                 goto set_transport;
456
457         /* Add messages to our internal queue */
458         iter = tny_list_create_iterator (headers);
459         while (!tny_iterator_is_done (iter)) {
460                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
461                 _add_message (self, header);
462                 g_object_unref (header);        
463                 tny_iterator_next (iter);
464         }
465
466         /* Reenable suspended items */
467         wakeup_op = modest_mail_operation_new (NULL);
468         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
469                                          wakeup_op);
470         modest_mail_operation_queue_wakeup (wakeup_op, MODEST_TNY_SEND_QUEUE (self));
471
472         /* Frees */
473         g_object_unref (iter);
474         g_object_unref (headers);
475
476  set_transport:
477         /* Do this at the end, because it'll call tny_send_queue_flush
478            which will call tny_send_queue_get_outbox and
479            tny_send_queue_get_sentbox */
480         tny_camel_send_queue_set_transport_account (TNY_CAMEL_SEND_QUEUE(self),
481                                                     info->account);
482
483         /* Frees */
484         g_object_unref (info->account); 
485         g_object_unref (info->queue); 
486         g_slice_free (GetHeadersInfo, info);
487 }
488
489 ModestTnySendQueue*
490 modest_tny_send_queue_new (TnyCamelTransportAccount *account)
491 {
492         ModestTnySendQueue *self = NULL;
493         ModestTnySendQueuePrivate *priv = NULL;
494         TnyList *headers = NULL;
495         GetHeadersInfo *info;
496
497         g_return_val_if_fail (TNY_IS_CAMEL_TRANSPORT_ACCOUNT(account), NULL);
498
499         self = MODEST_TNY_SEND_QUEUE(g_object_new(MODEST_TYPE_TNY_SEND_QUEUE, NULL));
500
501         /* Set outbox and sentbox */
502         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
503         priv->outbox  = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
504                                                                TNY_FOLDER_TYPE_OUTBOX);
505         priv->sentbox = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
506                                                                TNY_FOLDER_TYPE_SENT);
507
508         /* NOTE that this could happen if there was not enough disk
509            space when the account was created */
510         if (!priv->outbox || !priv->sentbox) {
511                 g_object_unref (self);
512                 return NULL;
513         }
514
515         /* Connect signals to control when a msg is being or has been sent */
516         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
517                                                        G_OBJECT(self),
518                                                        "msg-sending",
519                                                        G_CALLBACK(_on_msg_start_sending),
520                                                        NULL);
521         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
522                                                        G_OBJECT(self), "msg-sent",
523                                                        G_CALLBACK(_on_msg_has_been_sent),
524                                                        NULL);
525         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
526                                                        G_OBJECT(self), "error-happened",
527                                                        G_CALLBACK(_on_msg_error_happened),
528                                                        NULL);
529         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
530                                                        G_OBJECT (self), "queue-start",
531                                                        G_CALLBACK (_on_queue_start),
532                                                        NULL);
533         priv->sighandlers = modest_signal_mgr_connect (priv->sighandlers,
534                                                        G_OBJECT (self), "queue-stop",
535                                                        G_CALLBACK (_on_queue_stop),
536                                                        NULL);
537         priv->requested_send_receive = FALSE;
538
539         headers = tny_simple_list_new ();
540         info = g_slice_new0 (GetHeadersInfo);
541         info->account = g_object_ref (account);
542         info->queue = g_object_ref (self);
543         tny_folder_get_headers_async (priv->outbox, headers, TRUE, 
544                                       new_queue_get_headers_async_cb, 
545                                       NULL, info);
546
547         return self;
548 }
549
550 gboolean
551 modest_tny_send_queue_msg_is_being_sent (ModestTnySendQueue* self,
552                                          const gchar *msg_id)
553 {       
554         ModestTnySendQueueStatus status;
555         
556         g_return_val_if_fail (msg_id != NULL, FALSE); 
557         
558         status = modest_tny_send_queue_get_msg_status (self, msg_id);
559         return status == MODEST_TNY_SEND_QUEUE_SENDING;
560 }
561
562 gboolean
563 modest_tny_send_queue_sending_in_progress (ModestTnySendQueue* self)
564 {       
565         ModestTnySendQueuePrivate *priv;
566
567         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), FALSE);
568         
569         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
570         
571         return priv->sending;
572 }
573
574 ModestTnySendQueueStatus
575 modest_tny_send_queue_get_msg_status (ModestTnySendQueue *self, const gchar *msg_id)
576 {
577         GList *item;
578
579         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), MODEST_TNY_SEND_QUEUE_UNKNOWN);
580         g_return_val_if_fail (msg_id, MODEST_TNY_SEND_QUEUE_UNKNOWN);
581
582         item = modest_tny_send_queue_lookup_info (self, msg_id);
583         if (!item)
584                 return MODEST_TNY_SEND_QUEUE_UNKNOWN;
585         else
586                 return ((SendInfo*)item->data)->status;
587 }
588
589 gchar *
590 modest_tny_send_queue_get_msg_id (TnyHeader *header)
591 {
592         gchar* msg_uid = NULL;
593         gchar *subject;
594         time_t date_received;
595                 
596         g_return_val_if_fail (header && TNY_IS_HEADER(header), NULL);
597
598         /* Get message uid */
599         subject = tny_header_dup_subject (header);
600         date_received = tny_header_get_date_received (header);
601
602         msg_uid = g_strdup_printf ("%s %d", subject, (int) date_received);
603         g_free (subject);
604
605         return msg_uid;
606 }
607
608
609 static void
610 _on_msg_start_sending (TnySendQueue *self, TnyHeader *header,
611                        TnyMsg *msg, int done, int total, gpointer user_data)
612 {
613         ModestTnySendQueuePrivate *priv = NULL;
614         GList *item = NULL;
615         SendInfo *info = NULL;
616         gchar *msg_id = NULL;
617
618         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
619         
620         /* Get message uid */
621         msg_id = modest_tny_send_queue_get_msg_id (header);
622         if (msg_id) 
623                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
624         else
625                 g_warning ("%s: could not get msg-id for header", __FUNCTION__);
626         
627         if (item) {
628                 /* Set current status item */
629                 info = item->data;
630                 info->status = MODEST_TNY_SEND_QUEUE_SENDING;
631                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
632                 priv->current = item;
633         } else
634                 g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_id);
635         
636         /* free */
637         g_free (msg_id);
638 }
639
640 static void 
641 _on_msg_has_been_sent (TnySendQueue *self,
642                        TnyHeader *header,
643                        TnyMsg *msg, 
644                        int done, 
645                        int total,
646                        gpointer user_data)
647 {
648         ModestTnySendQueuePrivate *priv;
649         gchar *msg_id = NULL;
650         GList *item;
651
652         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
653
654         /* Get message uid */
655         msg_id = modest_tny_send_queue_get_msg_id (header);
656
657         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
658
659         tny_folder_sync_async (priv->sentbox, FALSE, NULL, NULL, NULL);
660
661         /* Get status info */
662         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
663
664
665         /* TODO: note that item=NULL must not happen, but I found that
666            tinymail is issuing the message-sent signal twice, because
667            tny_camel_send_queue_update is called twice for each
668            message sent. This must be fixed in tinymail. Sergio */
669         if (item) {
670                 /* Remove status info */
671                 modest_tny_send_queue_info_free (item->data);
672                 g_queue_delete_link (priv->queue, item);
673                 priv->current = NULL;
674                 
675                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_sent"));
676         }
677
678         /* free */
679         g_free(msg_id);
680 }
681
682 static void 
683 _on_msg_error_happened (TnySendQueue *self,
684                         TnyHeader *header,
685                         TnyMsg *msg,
686                         GError *err,
687                         gpointer user_data)
688 {
689         ModestTnySendQueuePrivate *priv = NULL;
690         
691         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
692
693         /* Note that header could be NULL. Tinymail notifies about
694            generic send queue errors with this signal as well, and
695            those notifications are not bound to any particular header
696            or message */
697         if (header && TNY_IS_HEADER (header)) {
698                 SendInfo *info = NULL;
699                 GList *item = NULL;
700                 gchar* msg_uid = NULL;
701
702                 /* Get sending info (create new if it doesn not exist) */
703                 msg_uid = modest_tny_send_queue_get_msg_id (header);
704                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
705                                                           msg_uid);
706
707                 /* TODO: this should not happen (but it does), so the
708                    problem should be located in the way we generate
709                    the message uids */
710                 if (!item) {
711                         g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_uid);
712                         g_free(msg_uid);
713                         return;
714                 }
715
716                 info = item->data;
717
718                 /* Keep in queue so that we remember that the opertion has failed */
719                 /* and was not just cancelled */
720                 if (err->code == TNY_SYSTEM_ERROR_CANCEL) {
721                         info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
722                 } else {
723                         info->status = MODEST_TNY_SEND_QUEUE_FAILED;
724                 }
725                 priv->current = NULL;
726
727                 /* Notify status has changed */
728                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
729
730                 /* free */
731                 g_free(msg_uid);
732         }
733 }
734
735 static void 
736 _on_queue_start (TnySendQueue *self,
737                  gpointer data)
738 {
739         ModestTnySendQueuePrivate *priv;
740         ModestMailOperation *mail_op;
741
742         mail_op = modest_mail_operation_new (NULL);
743         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
744                                          mail_op);
745         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
746         g_object_unref (mail_op);
747
748         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
749         priv->sending = TRUE;
750 }
751
752 static void 
753 _on_queue_stop (TnySendQueue *self,
754                 gpointer data)
755 {
756         ModestTnySendQueuePrivate *priv;
757
758         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
759         priv->sending = FALSE;
760 }
761
762 static void 
763 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
764 {
765         GSList **send_queues = (GSList **) userdata;
766         *send_queues = g_slist_prepend (*send_queues, value);
767 }
768
769 /* This function shouldn't be here. Move it to another place. Sergio */
770 ModestTnySendQueueStatus
771 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
772 {
773         ModestCacheMgr *cache_mgr = NULL;
774         GHashTable     *send_queue_cache = NULL;
775         ModestTnyAccountStore *accounts_store = NULL;
776         TnyList *accounts = NULL;
777         TnyIterator *iter = NULL;
778         TnyTransportAccount *account = NULL;
779         GSList *send_queues = NULL, *node;
780         /* get_msg_status returns suspended by default, so we want to detect changes */
781         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
782         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
783         gchar *msg_uid = NULL;
784         ModestTnySendQueue *send_queue = NULL;
785         
786         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
787
788         msg_uid = modest_tny_send_queue_get_msg_id (header);
789         cache_mgr = modest_runtime_get_cache_mgr ();
790         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
791                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
792         
793         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
794         if (send_queues == NULL) {
795                 accounts = tny_simple_list_new (); 
796                 accounts_store = modest_runtime_get_account_store ();
797                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
798                                                 accounts, 
799                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
800                 
801                 iter = tny_list_create_iterator (accounts);
802                 while (!tny_iterator_is_done (iter)) {                  
803                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
804                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
805                         g_object_unref(account);
806                         if (TNY_IS_SEND_QUEUE (send_queue)) {
807                                 queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
808                                 if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
809                                         status = queue_status;
810                                         break;
811                                 }
812                         }
813                         tny_iterator_next (iter);
814                 }
815                 g_object_unref (iter);
816                 g_object_unref (accounts);
817         }
818         else {
819                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
820                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
821                         
822                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
823                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
824                                 status = queue_status;
825                                 break;
826                         }
827                 }
828         }
829
830         g_free(msg_uid);
831         g_slist_free (send_queues);
832         return status;
833 }
834
835 typedef struct _WakeupHelper {
836         ModestTnySendQueue *self;
837         ModestTnySendQueueWakeupFunc callback;
838         gpointer userdata;
839 } WakeupHelper;
840
841 static void
842 wakeup_sync_cb (TnyFolder *self, gboolean cancelled, GError *err, gpointer userdata)
843 {
844         WakeupHelper *helper = (WakeupHelper *) userdata;
845
846         if (helper->callback) {
847                 helper->callback (helper->self, cancelled, err, helper->userdata);
848         }
849         g_object_unref (helper->self);
850         g_slice_free (WakeupHelper, helper);
851 }
852
853 static void
854 wakeup_get_headers_async_cb (TnyFolder *folder, 
855                              gboolean cancelled, 
856                              TnyList *headers, 
857                              GError *err, 
858                              gpointer user_data)
859 {
860         ModestTnySendQueue *self;
861         ModestTnySendQueuePrivate *priv;
862         TnyIterator *iter;
863         WakeupHelper *helper = (WakeupHelper *) user_data;
864
865         self = MODEST_TNY_SEND_QUEUE (helper->self);
866         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
867
868         if (cancelled || err) {
869                 g_debug ("Failed to wake up the headers of the send queue");
870                 g_object_unref (self);
871                 if (helper->callback) {
872                         helper->callback (helper->self, cancelled, err, helper->userdata);
873                 }
874                 g_object_unref (helper->self);
875                 g_slice_free (WakeupHelper, helper);
876                 return;
877         }
878
879         /* Wake up every single suspended header */
880         iter = tny_list_create_iterator (headers);
881         while (!tny_iterator_is_done (iter)) {
882                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
883
884                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
885                         gchar *msg_id;
886                         GList *item;
887                         SendInfo *info;
888
889                         /* Unset the suspended flag */
890                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
891
892                         /* Notify view */
893                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
894                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
895                         if (!item) {
896                                 info = g_slice_new (SendInfo);
897                                 info->msg_id = msg_id;
898                                 g_queue_push_tail (priv->queue, info);
899                         } else {
900                                 info = (SendInfo *) item->data;
901                                 g_free (msg_id);
902                         }
903                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
904                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);            
905                 }
906
907                 /* Frees */
908                 g_object_unref (header);
909                 tny_iterator_next (iter);
910         }
911
912         /* Make changes persistent on disk */
913         tny_folder_sync_async (priv->outbox, FALSE, wakeup_sync_cb, NULL, helper);
914
915         /* Frees */
916         g_object_unref (iter);
917         g_object_unref (headers);
918 }
919
920 void   
921 modest_tny_send_queue_wakeup (ModestTnySendQueue *self, 
922                               ModestTnySendQueueWakeupFunc callback,
923                               gpointer userdata)
924 {
925         ModestTnySendQueuePrivate *priv;
926         TnyList *headers;
927         WakeupHelper *helper;
928
929         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
930         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
931
932         helper = g_slice_new (WakeupHelper);
933         helper->self = g_object_ref (self);
934         helper->callback = callback;
935         helper->userdata = userdata;
936
937         headers = tny_simple_list_new ();
938         tny_folder_get_headers_async (priv->outbox, headers, TRUE, 
939                                       wakeup_get_headers_async_cb, 
940                                       NULL, helper);
941 }
942
943 gboolean 
944 modest_tny_send_queue_get_requested_send_receive (ModestTnySendQueue *self)
945 {
946         ModestTnySendQueuePrivate *priv;
947
948         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE (self), FALSE);
949         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
950
951         return priv->requested_send_receive;
952 }
953
954 void 
955 modest_tny_send_queue_set_requested_send_receive (ModestTnySendQueue *self, gboolean requested_send_receive)
956 {
957         ModestTnySendQueuePrivate *priv;
958
959         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
960         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
961
962         priv->requested_send_receive = requested_send_receive;
963 }