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