* Partially fixes NB#85031. At least it fixes comment #1
[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_new0 (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 typedef struct {
402         TnyCamelTransportAccount *account;
403         ModestTnySendQueue *queue;
404 } GetHeadersInfo;
405
406 static void
407 new_queue_get_headers_async_cb (TnyFolder *folder, 
408                                 gboolean cancelled, 
409                                 TnyList *headers, 
410                                 GError *err, 
411                                 gpointer user_data)
412 {
413         ModestTnySendQueue *self;
414         TnyIterator *iter;
415         GetHeadersInfo *info;
416
417         info = (GetHeadersInfo *) user_data;
418         self = MODEST_TNY_SEND_QUEUE (info->queue);
419
420         /* In case of error set the transport account anyway */
421         if (cancelled || err)
422                 goto set_transport;
423
424         /* Add messages to our internal queue */
425         iter = tny_list_create_iterator (headers);
426         while (!tny_iterator_is_done (iter)) {
427                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
428                 _add_message (self, header);
429                 g_object_unref (header);        
430                 tny_iterator_next (iter);
431         }
432
433         /* Reenable suspended items */
434         modest_tny_send_queue_wakeup (self);
435
436         /* Frees */
437         g_object_unref (iter);
438         g_object_unref (headers);
439
440  set_transport:
441         /* Do this at the end, because it'll call tny_send_queue_flush
442            which will call tny_send_queue_get_outbox and
443            tny_send_queue_get_sentbox */
444         tny_camel_send_queue_set_transport_account (TNY_CAMEL_SEND_QUEUE(self),
445                                                     info->account);
446
447         /* Frees */
448         g_object_unref (info->account); 
449         g_object_unref (info->queue); 
450         g_slice_free (GetHeadersInfo, info);
451 }
452
453 ModestTnySendQueue*
454 modest_tny_send_queue_new (TnyCamelTransportAccount *account)
455 {
456         ModestTnySendQueue *self = NULL;
457         ModestTnySendQueuePrivate *priv = NULL;
458         TnyList *headers = NULL;
459         GetHeadersInfo *info;
460         
461         g_return_val_if_fail (TNY_IS_CAMEL_TRANSPORT_ACCOUNT(account), NULL);
462         
463         self = MODEST_TNY_SEND_QUEUE(g_object_new(MODEST_TYPE_TNY_SEND_QUEUE, NULL));
464         
465         /* Connect signals to control when a msg is being or has been sent */
466         g_signal_connect (G_OBJECT(self), "msg-sending",
467                           G_CALLBACK(_on_msg_start_sending),
468                           NULL);                          
469         g_signal_connect (G_OBJECT(self), "msg-sent",
470                           G_CALLBACK(_on_msg_has_been_sent), 
471                           NULL);
472         g_signal_connect (G_OBJECT(self), "error-happened",
473                           G_CALLBACK(_on_msg_error_happened),
474                           NULL);
475         g_signal_connect (G_OBJECT (self), "queue-start",
476                           G_CALLBACK (_on_queue_start),
477                           NULL);
478
479         /* Set outbox and sentbox */
480         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
481         priv->outbox  = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
482                                                                TNY_FOLDER_TYPE_OUTBOX);
483         priv->sentbox = modest_tny_account_get_special_folder (TNY_ACCOUNT(account),
484                                                                TNY_FOLDER_TYPE_SENT);
485         priv->requested_send_receive = FALSE;
486
487         headers = tny_simple_list_new ();
488         info = g_slice_new0 (GetHeadersInfo);
489         info->account = g_object_ref (account);
490         info->queue = g_object_ref (self);
491         tny_folder_get_headers_async (priv->outbox, headers, TRUE, 
492                                       new_queue_get_headers_async_cb, 
493                                       NULL, info);
494
495         return self;
496 }
497
498 gboolean
499 modest_tny_send_queue_msg_is_being_sent (ModestTnySendQueue* self,
500                                          const gchar *msg_id)
501 {       
502         ModestTnySendQueueStatus status;
503         
504         g_return_val_if_fail (msg_id != NULL, FALSE); 
505         
506         status = modest_tny_send_queue_get_msg_status (self, msg_id);
507         return status == MODEST_TNY_SEND_QUEUE_SENDING;
508 }
509
510 gboolean
511 modest_tny_send_queue_sending_in_progress (ModestTnySendQueue* self)
512 {       
513         ModestTnySendQueuePrivate *priv;
514
515         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), FALSE);
516         
517         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
518         
519         return priv->current != NULL;
520 }
521
522 ModestTnySendQueueStatus
523 modest_tny_send_queue_get_msg_status (ModestTnySendQueue *self, const gchar *msg_id)
524 {
525         GList *item;
526
527         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE(self), MODEST_TNY_SEND_QUEUE_UNKNOWN);
528         g_return_val_if_fail (msg_id, MODEST_TNY_SEND_QUEUE_UNKNOWN);
529
530         item = modest_tny_send_queue_lookup_info (self, msg_id);
531         if (!item)
532                 return MODEST_TNY_SEND_QUEUE_UNKNOWN;
533         else
534                 return ((SendInfo*)item->data)->status;
535 }
536
537 gchar *
538 modest_tny_send_queue_get_msg_id (TnyHeader *header)
539 {
540         gchar* msg_uid = NULL;
541         gchar *subject;
542         time_t date_received;
543                 
544         g_return_val_if_fail (header && TNY_IS_HEADER(header), NULL);
545
546         /* Get message uid */
547         subject = tny_header_dup_subject (header);
548         date_received = tny_header_get_date_received (header);
549
550         msg_uid = g_strdup_printf ("%s %d", subject, (int) date_received);
551         g_free (subject);
552
553         return msg_uid;
554 }
555
556
557 static void
558 _on_msg_start_sending (TnySendQueue *self, TnyHeader *header,
559                        TnyMsg *msg, int done, int total, gpointer user_data)
560 {
561         ModestTnySendQueuePrivate *priv = NULL;
562         GList *item = NULL;
563         SendInfo *info = NULL;
564         gchar *msg_id = NULL;
565
566         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
567         
568         /* Get message uid */
569         msg_id = modest_tny_send_queue_get_msg_id (header);
570         if (msg_id) 
571                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
572         else
573                 g_warning ("%s: could not get msg-id for header", __FUNCTION__);
574         
575         if (item) {
576                 /* Set current status item */
577                 info = item->data;
578                 info->status = MODEST_TNY_SEND_QUEUE_SENDING;
579                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
580                 priv->current = item;
581         } else
582                 g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_id);
583         
584         /* free */
585         g_free (msg_id);
586 }
587
588 static void 
589 _on_msg_has_been_sent (TnySendQueue *self,
590                        TnyHeader *header,
591                        TnyMsg *msg, 
592                        int done, 
593                        int total,
594                        gpointer user_data)
595 {
596         ModestTnySendQueuePrivate *priv;
597         gchar *msg_id = NULL;
598         GList *item;
599
600         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
601
602         /* Get message uid */
603         msg_id = modest_tny_send_queue_get_msg_id (header);
604
605         tny_header_set_flag (header, TNY_HEADER_FLAG_SEEN);
606
607         tny_folder_sync_async (priv->sentbox, FALSE, NULL, NULL, NULL);
608
609         /* Get status info */
610         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
611
612
613         /* TODO: note that item=NULL must not happen, but I found that
614            tinymail is issuing the message-sent signal twice, because
615            tny_camel_send_queue_update is called twice for each
616            message sent. This must be fixed in tinymail. Sergio */
617         if (item) {
618                 /* Remove status info */
619                 modest_tny_send_queue_info_free (item->data);
620                 g_queue_delete_link (priv->queue, item);
621                 priv->current = NULL;
622                 
623                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_sent"));
624         }
625
626         /* free */
627         g_free(msg_id);
628 }
629
630 static void 
631 _on_msg_error_happened (TnySendQueue *self,
632                         TnyHeader *header,
633                         TnyMsg *msg,
634                         GError *err,
635                         gpointer user_data)
636 {
637         ModestTnySendQueuePrivate *priv = NULL;
638         
639         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
640
641         /* Note that header could be NULL. Tinymail notifies about
642            generic send queue errors with this signal as well, and
643            those notifications are not bound to any particular header
644            or message */
645         if (header && TNY_IS_HEADER (header)) {
646                 SendInfo *info = NULL;
647                 GList *item = NULL;
648                 gchar* msg_uid = NULL;
649
650                 /* Get sending info (create new if it doesn not exist) */
651                 msg_uid = modest_tny_send_queue_get_msg_id (header);
652                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
653                                                           msg_uid);
654
655                 /* TODO: this should not happen (but it does), so the
656                    problem should be located in the way we generate
657                    the message uids */
658                 if (!item) {
659                         g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_uid);
660                         g_free(msg_uid);
661                         return;
662                 }
663                 
664                 info = item->data;
665
666                 /* Keep in queue so that we remember that the opertion has failed */
667                 /* and was not just cancelled */
668                 if (err->code == TNY_SYSTEM_ERROR_CANCEL)
669                         info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
670                 else
671                         info->status = MODEST_TNY_SEND_QUEUE_FAILED;
672                 priv->current = NULL;
673                 
674                 /* Notify status has changed */
675                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
676
677                 /* free */
678                 g_free(msg_uid);
679         }
680 }
681
682 static void 
683 _on_queue_start (TnySendQueue *self,
684                  gpointer data)
685 {
686         ModestMailOperation *mail_op;
687
688         mail_op = modest_mail_operation_new (NULL);
689         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
690                                          mail_op);
691         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
692         g_object_unref (mail_op);
693 }
694
695 static void 
696 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
697 {
698         GSList **send_queues = (GSList **) userdata;
699         *send_queues = g_slist_prepend (*send_queues, value);
700 }
701
702 /* This function shouldn't be here. Move it to another place. Sergio */
703 ModestTnySendQueueStatus
704 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
705 {
706         ModestCacheMgr *cache_mgr = NULL;
707         GHashTable     *send_queue_cache = NULL;
708         ModestTnyAccountStore *accounts_store = NULL;
709         TnyList *accounts = NULL;
710         TnyIterator *iter = NULL;
711         TnyTransportAccount *account = NULL;
712         GSList *send_queues = NULL, *node;
713         /* get_msg_status returns suspended by default, so we want to detect changes */
714         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
715         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
716         gchar *msg_uid = NULL;
717         ModestTnySendQueue *send_queue = NULL;
718         
719         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
720
721         msg_uid = modest_tny_send_queue_get_msg_id (header);
722         cache_mgr = modest_runtime_get_cache_mgr ();
723         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
724                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
725         
726         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
727         if (send_queues == NULL) {
728                 accounts = tny_simple_list_new (); 
729                 accounts_store = modest_runtime_get_account_store ();
730                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
731                                                 accounts, 
732                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
733                 
734                 iter = tny_list_create_iterator (accounts);
735                 while (!tny_iterator_is_done (iter)) {                  
736                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
737                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account), TRUE);
738                         g_object_unref(account);
739
740                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
741                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
742                                 status = queue_status;
743                                 break;
744                         }
745                         tny_iterator_next (iter);
746                 }
747                 g_object_unref (iter);
748                 g_object_unref (accounts);
749         }
750         else {
751                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
752                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
753                         
754                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
755                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
756                                 status = queue_status;
757                                 break;
758                         }
759                 }
760         }
761
762         g_free(msg_uid);
763         g_slist_free (send_queues);
764         return status;
765 }
766
767 static void
768 wakeup_get_headers_async_cb (TnyFolder *folder, 
769                              gboolean cancelled, 
770                              TnyList *headers, 
771                              GError *err, 
772                              gpointer user_data)
773 {
774         ModestTnySendQueue *self;
775         ModestTnySendQueuePrivate *priv;
776         TnyIterator *iter;
777
778         self = MODEST_TNY_SEND_QUEUE (user_data);
779         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
780
781         if (cancelled || err) {
782                 g_debug ("Failed to wake up the headers of the send queue");
783                 g_object_unref (self);
784                 return;
785         }
786
787         /* Wake up every single suspended header */
788         iter = tny_list_create_iterator (headers);
789         while (!tny_iterator_is_done (iter)) {
790                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
791
792                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
793                         gchar *msg_id;
794                         GList *item;
795                         SendInfo *info;
796
797                         /* Unset the suspended flag */
798                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
799
800                         /* Notify view */
801                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
802                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
803                         info = (SendInfo *) item->data;
804                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
805                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
806                         
807                         /* Frees */
808                         g_free (msg_id);
809                 }
810
811                 /* Frees */
812                 g_object_unref (header);
813                 tny_iterator_next (iter);
814         }
815
816         /* Make changes persistent on disk */
817         tny_folder_sync_async (priv->outbox, FALSE, NULL, NULL, NULL);
818
819         /* Frees */
820         g_object_unref (iter);
821         g_object_unref (headers);
822         g_object_unref (self);
823 }
824
825
826 void   
827 modest_tny_send_queue_wakeup (ModestTnySendQueue *self)
828 {
829         ModestTnySendQueuePrivate *priv;
830         TnyList *headers;
831
832         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
833
834         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
835
836         headers = tny_simple_list_new ();
837         tny_folder_get_headers_async (priv->outbox, headers, TRUE, 
838                                       wakeup_get_headers_async_cb, 
839                                       NULL, g_object_ref (self));
840 }
841
842 gboolean 
843 modest_tny_send_queue_get_requested_send_receive (ModestTnySendQueue *self)
844 {
845         ModestTnySendQueuePrivate *priv;
846
847         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE (self), FALSE);
848         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
849
850         return priv->requested_send_receive;
851 }
852
853 void 
854 modest_tny_send_queue_set_requested_send_receive (ModestTnySendQueue *self, gboolean requested_send_receive)
855 {
856         ModestTnySendQueuePrivate *priv;
857
858         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
859         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
860
861         priv->requested_send_receive = requested_send_receive;
862 }