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