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