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