* Fixes NB#83408, select attachment is now set as children of the edit window
[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         /* Get status info */
576         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
577
578
579         /* TODO: note that item=NULL must not happen, but I found that
580            tinymail is issuing the message-sent signal twice, because
581            tny_camel_send_queue_update is called twice for each
582            message sent. This must be fixed in tinymail. Sergio */
583         if (item) {
584                 /* Remove status info */
585                 modest_tny_send_queue_info_free (item->data);
586                 g_queue_delete_link (priv->queue, item);
587                 priv->current = NULL;
588                 
589                 modest_platform_information_banner (NULL, NULL, _("mcen_ib_message_sent"));
590         }
591
592         /* free */
593         g_free(msg_id);
594 }
595
596 static void 
597 _on_msg_error_happened (TnySendQueue *self,
598                         TnyHeader *header,
599                         TnyMsg *msg,
600                         GError *err,
601                         gpointer user_data)
602 {
603         ModestTnySendQueuePrivate *priv = NULL;
604         
605         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
606
607         /* Note that header could be NULL. Tinymail notifies about
608            generic send queue errors with this signal as well, and
609            those notifications are not bound to any particular header
610            or message */
611         if (header && TNY_IS_HEADER (header)) {
612                 SendInfo *info = NULL;
613                 GList *item = NULL;
614                 gchar* msg_uid = NULL;
615
616                 /* Get sending info (create new if it doesn not exist) */
617                 msg_uid = modest_tny_send_queue_get_msg_id (header);
618                 item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), 
619                                                           msg_uid);
620
621                 /* TODO: this should not happen (but it does), so the
622                    problem should be located in the way we generate
623                    the message uids */
624                 if (!item) {
625                         g_warning ("%s: could not find item with id '%s'", __FUNCTION__, msg_uid);
626                         g_free(msg_uid);
627                         return;
628                 }
629                 
630                 info = item->data;
631
632                 /* Keep in queue so that we remember that the opertion has failed */
633                 /* and was not just cancelled */
634                 if (err->code == TNY_SYSTEM_ERROR_CANCEL)
635                         info->status = MODEST_TNY_SEND_QUEUE_SUSPENDED;
636                 else
637                         info->status = MODEST_TNY_SEND_QUEUE_FAILED;
638                 priv->current = NULL;
639                 
640                 /* Notify status has changed */
641                 g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
642
643                 /* free */
644                 g_free(msg_uid);
645         }
646 }
647
648 static void 
649 _on_queue_start (TnySendQueue *self,
650                  gpointer data)
651 {
652         ModestMailOperation *mail_op;
653
654         mail_op = modest_mail_operation_new (NULL);
655         modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
656                                          mail_op);
657         modest_mail_operation_run_queue (mail_op, MODEST_TNY_SEND_QUEUE (self));
658         g_object_unref (mail_op);
659 }
660
661 static void 
662 fill_list_of_caches (gpointer key, gpointer value, gpointer userdata)
663 {
664         GSList **send_queues = (GSList **) userdata;
665         *send_queues = g_slist_prepend (*send_queues, value);
666 }
667
668 /* This function shouldn't be here. Move it to another place. Sergio */
669 ModestTnySendQueueStatus
670 modest_tny_all_send_queues_get_msg_status (TnyHeader *header)
671 {
672         ModestCacheMgr *cache_mgr = NULL;
673         GHashTable     *send_queue_cache = NULL;
674         ModestTnyAccountStore *accounts_store = NULL;
675         TnyList *accounts = NULL;
676         TnyIterator *iter = NULL;
677         TnyTransportAccount *account = NULL;
678         GSList *send_queues = NULL, *node;
679         /* get_msg_status returns suspended by default, so we want to detect changes */
680         ModestTnySendQueueStatus status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
681         ModestTnySendQueueStatus queue_status = MODEST_TNY_SEND_QUEUE_UNKNOWN;
682         gchar *msg_uid = NULL;
683         ModestTnySendQueue *send_queue = NULL;
684         
685         g_return_val_if_fail (TNY_IS_HEADER(header), MODEST_TNY_SEND_QUEUE_UNKNOWN);
686
687         msg_uid = modest_tny_send_queue_get_msg_id (header);
688         cache_mgr = modest_runtime_get_cache_mgr ();
689         send_queue_cache = modest_cache_mgr_get_cache (cache_mgr,
690                                                        MODEST_CACHE_MGR_CACHE_TYPE_SEND_QUEUE);
691         
692         g_hash_table_foreach (send_queue_cache, (GHFunc) fill_list_of_caches, &send_queues);
693         if (send_queues == NULL) {
694                 accounts = tny_simple_list_new (); 
695                 accounts_store = modest_runtime_get_account_store ();
696                 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(accounts_store), 
697                                                 accounts, 
698                                                 TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
699                 
700                 iter = tny_list_create_iterator (accounts);
701                 while (!tny_iterator_is_done (iter)) {                  
702                         account = TNY_TRANSPORT_ACCOUNT(tny_iterator_get_current (iter));
703                         send_queue = modest_runtime_get_send_queue(TNY_TRANSPORT_ACCOUNT(account));
704                         g_object_unref(account);
705
706                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
707                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
708                                 status = queue_status;
709                                 break;
710                         }
711                         tny_iterator_next (iter);
712                 }
713                 g_object_unref (iter);
714                 g_object_unref (accounts);
715         }
716         else {
717                 for (node = send_queues; node != NULL; node = g_slist_next (node)) {
718                         send_queue = MODEST_TNY_SEND_QUEUE (node->data);
719                         
720                         queue_status = modest_tny_send_queue_get_msg_status (send_queue, msg_uid);
721                         if (queue_status != MODEST_TNY_SEND_QUEUE_UNKNOWN) {
722                                 status = queue_status;
723                                 break;
724                         }
725                 }
726         }
727
728         g_free(msg_uid);
729         g_slist_free (send_queues);
730         return status;
731 }
732
733 void   
734 modest_tny_send_queue_wakeup (ModestTnySendQueue *self)
735 {
736         ModestTnySendQueuePrivate *priv;
737         TnyList *headers;
738         TnyIterator *iter;
739
740         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
741
742         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
743
744         headers = tny_simple_list_new ();
745         tny_folder_get_headers (priv->outbox, headers, TRUE, NULL);
746
747         /* Wake up every single suspended header */
748         iter = tny_list_create_iterator (headers);
749         while (!tny_iterator_is_done (iter)) {
750                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
751
752                 if (tny_header_get_flags (header) & TNY_HEADER_FLAG_SUSPENDED) {
753                         gchar *msg_id;
754                         GList *item;
755                         SendInfo *info;
756
757                         /* Unset the suspended flag */
758                         tny_header_unset_flag (header, TNY_HEADER_FLAG_SUSPENDED);
759
760                         /* Notify view */
761                         msg_id = modest_tny_send_queue_get_msg_id (header);                     
762                         item = modest_tny_send_queue_lookup_info (MODEST_TNY_SEND_QUEUE (self), msg_id);
763                         info = (SendInfo *) item->data;
764                         info->status = MODEST_TNY_SEND_QUEUE_WAITING;
765                         g_signal_emit (self, signals[STATUS_CHANGED_SIGNAL], 0, info->msg_id, info->status);
766                         
767                         /* Frees */
768                         g_free (msg_id);
769                 }
770
771                 /* Frees */
772                 g_object_unref (header);
773                 tny_iterator_next (iter);
774         }
775
776         /* Make changes persistent on disk */
777         tny_folder_sync_async (priv->outbox, FALSE, NULL, NULL, NULL);
778
779         /* Frees */
780         g_object_unref (iter);
781         g_object_unref (G_OBJECT (headers));
782 }
783
784 gboolean 
785 modest_tny_send_queue_get_requested_send_receive (ModestTnySendQueue *self)
786 {
787         ModestTnySendQueuePrivate *priv;
788
789         g_return_val_if_fail (MODEST_IS_TNY_SEND_QUEUE (self), FALSE);
790         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
791
792         return priv->requested_send_receive;
793 }
794
795 void 
796 modest_tny_send_queue_set_requested_send_receive (ModestTnySendQueue *self, gboolean requested_send_receive)
797 {
798         ModestTnySendQueuePrivate *priv;
799
800         g_return_if_fail (MODEST_IS_TNY_SEND_QUEUE (self));
801         priv = MODEST_TNY_SEND_QUEUE_GET_PRIVATE (self);
802
803         priv->requested_send_receive = requested_send_receive;
804 }