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