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