* fix the url handling of '&'; it's a bit hairy because of the
[modest] / libmodest-dbus-client / libmodest-dbus-client.c
1 /* Copyright (c) 2007, 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 #include "libmodest-dbus-client.h"
31 #include <dbus_api/modest-dbus-api.h> /* For the API strings. */
32
33 //#define DBUS_API_SUBJECT_TO_CHANGE 1
34 #include <dbus/dbus.h>
35 #include <dbus/dbus-glib-lowlevel.h>
36 #include <string.h>
37
38
39
40 /** Get a comma-separated list of attachement URI strings, 
41  * from a list of strings.
42  */
43 static gchar* get_attachments_string (GSList *attachments)
44 {
45         if (!attachments)
46                 return NULL;
47                 
48         gchar *attachments_str = g_strdup("");
49         
50         GSList *iter = attachments;
51         while (iter)
52         {
53                 if (iter->data) {
54                         gchar *tmp = g_strconcat(attachments_str, ",", (gchar *) (iter->data), NULL);
55                         g_free(attachments_str);
56                         attachments_str = tmp;
57                 }
58                 
59                 iter = g_slist_next(iter);
60         }
61         
62         return attachments_str;
63 }
64         
65 /**
66  * libmodest_dbus_client_mail_to:
67  * @osso_context: a valid #osso_context_t object.
68  * @mailto_uri: A mailto URI.
69  * 
70  * This function will try to do a remote procedure call (rpc)
71  * into modest (or start it if necessary) and open a composer
72  * window with the supplied parameters prefilled.
73  *
74  * Return value: Whether or not the rpc call to modest
75  * was successfull
76  **/
77 gboolean 
78 libmodest_dbus_client_mail_to (osso_context_t *osso_context, const gchar *mailto_uri)
79 {
80         osso_rpc_t retval;
81         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
82                    MODEST_DBUS_NAME, 
83                    MODEST_DBUS_METHOD_MAIL_TO, &retval, 
84                    DBUS_TYPE_STRING, mailto_uri, 
85                    DBUS_TYPE_INVALID);
86                 
87         if (ret != OSSO_OK) {
88                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
89                 return FALSE;
90         } else {
91                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
92         }
93         
94         osso_rpc_free_val(&retval);
95         
96         return TRUE;
97 }
98
99 /**
100  * libmodest_dbus_client_compose_mail:
101  * @osso_context: a valid #osso_context_t object.
102  * @to: The Recipients (From: line)
103  * @cc: Recipients for carbon copies
104  * @bcc: Recipients for blind carbon copies
105  * @subject: Subject line
106  * @body: The actual body of the mail to compose.
107  * @attachments: Additional list of attachments. A list of URI strings.
108  * 
109  * This function will try to do a remote procedure call (rpc)
110  * into modest (or start it if necessary) and open a composer
111  * window with the supplied parameters prefilled.
112  *
113  * Return value: Whether or not the rpc call to modest
114  * was successfull
115  **/
116 gboolean
117 libmodest_dbus_client_compose_mail (osso_context_t *osso_context, const gchar *to, const gchar *cc, 
118         const gchar *bcc, const gchar* subject, const gchar* body, GSList *attachments)
119 {
120         osso_rpc_t retval;
121         
122         gchar *attachments_str = get_attachments_string(attachments);
123
124         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
125                    MODEST_DBUS_NAME, 
126                    MODEST_DBUS_METHOD_COMPOSE_MAIL, &retval, 
127                    DBUS_TYPE_STRING, to, 
128                    DBUS_TYPE_STRING, cc, 
129                    DBUS_TYPE_STRING, bcc, 
130                    DBUS_TYPE_STRING, subject, 
131                    DBUS_TYPE_STRING, body,
132                    DBUS_TYPE_STRING, attachments_str,
133                    DBUS_TYPE_INVALID);
134                 
135         if (ret != OSSO_OK) {
136                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
137                 return FALSE;
138         } else {
139                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
140         }
141         
142         osso_rpc_free_val(&retval);
143
144         g_free (attachments_str);
145         
146         return TRUE;
147 }
148
149 /**
150  * libmodest_dbus_client_open_message:
151  * @osso_context: a valid #osso_context_t object.
152  * @msg_uri: A valid url to a mail
153  *
154  * This method will try to find the message supplied
155  * by @msg_uri and open it for display if found. 
156  * It will use remote procedure calls (rpc) over 
157  * dbus to do so.
158  *  
159  * Return value: TRUE on successs, FALSE on error
160  **/
161 gboolean 
162 libmodest_dbus_client_open_message (osso_context_t *osso_context, const gchar *mail_uri)
163 {
164         osso_rpc_t retval;
165         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
166                    MODEST_DBUS_NAME, 
167                    MODEST_DBUS_METHOD_OPEN_MESSAGE, &retval, 
168                    DBUS_TYPE_STRING, mail_uri, 
169                    DBUS_TYPE_INVALID);
170                 
171         if (ret != OSSO_OK) {
172                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
173                 return FALSE;
174         } else {
175                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
176         }
177         
178         osso_rpc_free_val(&retval);
179         
180         return TRUE;
181 }
182
183 gboolean 
184 libmodest_dbus_client_send_and_receive (osso_context_t *osso_context)
185 {
186         osso_rpc_t retval;
187         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
188                    MODEST_DBUS_NAME, 
189                    MODEST_DBUS_METHOD_SEND_RECEIVE, &retval, 
190                    DBUS_TYPE_INVALID);
191                 
192         if (ret != OSSO_OK) {
193                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
194                 return FALSE;
195         } else {
196                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
197         }
198         
199         osso_rpc_free_val(&retval);
200         
201         return TRUE;
202 }
203
204 gboolean 
205 libmodest_dbus_client_open_default_inbox (osso_context_t *osso_context)
206 {
207         osso_rpc_t retval;
208         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
209                    MODEST_DBUS_NAME, 
210                    MODEST_DBUS_METHOD_OPEN_DEFAULT_INBOX, &retval, 
211                    DBUS_TYPE_INVALID);
212                 
213         if (ret != OSSO_OK) {
214                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
215                 return FALSE;
216         } else {
217                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
218         }
219         
220         osso_rpc_free_val(&retval);
221         
222         return TRUE;
223 }
224
225 /**
226  * libmodest_dbus_client_delete_message:
227  * @osso_context: a valid #osso_context_t object.
228  * @msg_uri: A valid url to a mail 
229  *
230  * This method will try to find the message supplied
231  * by @msg_uri and if found delete it. It will use
232  * remote procedure calls (rpc) over dbus to do so.
233  * 
234  * Return value: TRUE on successs, FALSE on error
235  **/
236 gboolean
237 libmodest_dbus_client_delete_message (osso_context_t   *osso_ctx,
238                                       const char       *msg_uri)
239 {
240         osso_rpc_t    retval;
241         osso_return_t ret;
242        
243         ret = osso_rpc_run_with_defaults (osso_ctx, 
244                                           MODEST_DBUS_NAME, 
245                                           MODEST_DBUS_METHOD_DELETE_MESSAGE, &retval, 
246                                           DBUS_TYPE_STRING, msg_uri, 
247                                           DBUS_TYPE_INVALID);
248                 
249         if (ret != OSSO_OK) {
250                 g_debug ("debug: osso_rpc_run() failed.\n");
251         } else {
252                 g_debug ("debug: osso_rpc_run() succeeded.\n");
253         }
254         
255         osso_rpc_free_val (&retval);
256
257         return ret == OSSO_OK;
258 }
259
260 static void
261 modest_search_hit_free (ModestSearchHit *hit)
262 {
263         g_free (hit->msgid);
264         g_slice_free (ModestSearchHit, hit);
265 }
266
267 void
268 modest_search_hit_list_free (GList *hits)
269 {
270         GList *iter;
271
272         if (hits == NULL) {
273                 return;
274         }
275
276         for (iter = hits; iter; iter = iter->next) {
277                 modest_search_hit_free ((ModestSearchHit *) iter->data);
278         }
279
280         g_list_free (hits);
281 }
282
283 static char *
284 _dbus_iter_get_string_or_null (DBusMessageIter *iter)
285 {
286         const char *string = NULL;
287         char       *ret = NULL;
288
289         dbus_message_iter_get_basic (iter, &string);
290         
291         if (string && strlen (string)) {
292                 ret = g_strdup (string);
293         }
294
295         return ret;
296 }
297
298 static guint64
299 _dbus_iter_get_uint64 (DBusMessageIter *iter)
300 {
301         dbus_uint64_t ui64v;
302         guint64       ret;
303
304         ui64v = 0;
305         dbus_message_iter_get_basic (iter, &ui64v);
306
307         ret = (guint64) ui64v;
308
309         return ret;
310 }
311
312
313 static gint64
314 _dbus_iter_get_int64 (DBusMessageIter *iter)
315 {
316         dbus_int64_t i64v;
317         gint64       ret;
318
319         i64v = 0;
320         dbus_message_iter_get_basic (iter, &i64v);
321
322         ret = (gint64) i64v;
323
324         return ret;
325 }
326
327 static gboolean
328 _dbus_iter_get_boolean (DBusMessageIter *iter)
329
330 {
331         dbus_bool_t  val;
332         gboolean     ret;
333
334         val = FALSE;
335         dbus_message_iter_get_basic (iter, &val);
336
337         ret = (gboolean) val;
338
339         return ret;
340 }
341
342 /** Get the values from the complex type (SEARCH_HIT_DBUS_TYPE)
343  * in the D-Bus return message. */
344 static ModestSearchHit *
345 modest_dbus_message_iter_get_search_hit (DBusMessageIter *parent)
346 {
347         ModestSearchHit *hit;
348         DBusMessageIter  child;
349         dbus_bool_t      res;
350         int              arg_type;
351         gboolean         error;
352
353         error = FALSE;
354         hit = g_slice_new0 (ModestSearchHit);
355
356         arg_type = dbus_message_iter_get_arg_type (parent);
357
358         if (arg_type != 'r') {
359                 return NULL;
360         }
361
362         dbus_message_iter_recurse (parent, &child);
363         
364         /* msgid  */
365         arg_type = dbus_message_iter_get_arg_type (&child);
366
367         if (arg_type != DBUS_TYPE_STRING) {
368                 error = TRUE;
369                 goto out;
370         }
371
372         hit->msgid = _dbus_iter_get_string_or_null (&child);
373
374         res = dbus_message_iter_next (&child);
375         if (res == FALSE) {
376                 error = TRUE;
377                 goto out;
378         }
379
380         /* subject  */
381         arg_type = dbus_message_iter_get_arg_type (&child);
382
383         if (arg_type != DBUS_TYPE_STRING) {
384                 error = TRUE;
385                 goto out;
386         }
387
388         hit->subject = _dbus_iter_get_string_or_null (&child);
389
390         res = dbus_message_iter_next (&child);
391         if (res == FALSE) {
392                 error = TRUE;
393                 goto out;
394         }
395
396         /* folder  */
397         arg_type = dbus_message_iter_get_arg_type (&child);
398
399         if (arg_type != DBUS_TYPE_STRING) {
400                 error = TRUE;
401                 goto out;
402         }
403
404         hit->folder = _dbus_iter_get_string_or_null (&child);
405
406         res = dbus_message_iter_next (&child);
407         if (res == FALSE) {
408                 error = TRUE;
409                 goto out;
410         }
411
412         /* sender  */
413         arg_type = dbus_message_iter_get_arg_type (&child);
414
415         if (arg_type != DBUS_TYPE_STRING) {
416                 error = TRUE;
417                 goto out;
418         }
419
420         hit->sender = _dbus_iter_get_string_or_null (&child);
421
422         res = dbus_message_iter_next (&child);
423         if (res == FALSE) {
424                 error = TRUE;
425                 goto out;
426         }
427
428         /* msize  */
429         arg_type = dbus_message_iter_get_arg_type (&child);
430
431         if (arg_type != DBUS_TYPE_UINT64) {
432                 error = TRUE;
433                 goto out;
434         }
435
436         hit->msize = _dbus_iter_get_uint64 (&child);
437
438         res = dbus_message_iter_next (&child);
439         if (res == FALSE) {
440                 error = TRUE;
441                 goto out;
442         }
443
444         /* has_attachment  */
445         arg_type = dbus_message_iter_get_arg_type (&child);
446
447         if (arg_type != DBUS_TYPE_BOOLEAN) {
448                 error = TRUE;
449                 goto out;
450         }
451
452         hit->has_attachment = _dbus_iter_get_boolean (&child); 
453
454         res = dbus_message_iter_next (&child);
455         if (res == FALSE) {
456                 error = TRUE;
457                 goto out;
458         }
459
460         /* is_unread  */
461         arg_type = dbus_message_iter_get_arg_type (&child);
462
463         if (arg_type != DBUS_TYPE_BOOLEAN) {
464                 error = TRUE;
465                 goto out;
466         }
467
468         hit->is_unread = _dbus_iter_get_boolean (&child);  
469
470         res = dbus_message_iter_next (&child);
471         if (res == FALSE) {
472                 error = TRUE;
473                 goto out;
474         }
475
476         /* timestamp  */
477         arg_type = dbus_message_iter_get_arg_type (&child);
478
479         if (arg_type != DBUS_TYPE_INT64) {
480                 error = TRUE;
481                 goto out;
482         }
483
484         hit->timestamp = _dbus_iter_get_int64 (&child); 
485
486         res = dbus_message_iter_next (&child);
487         if (res == TRUE) {
488                 error = TRUE;
489                 goto out;
490         }       
491
492 out:
493         if (error) {
494                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
495                 modest_search_hit_free (hit);
496                 hit = NULL;
497         }
498
499         return hit;
500 }
501
502 /**
503  * libmodest_dbus_client_search:
504  * @osso_ctx: A valid #osso_context_t object.
505  * @query: The term to search for.
506  * @folder: An url to specific folder or %NULL to search everywhere.
507  * @start_date: Search hits before this date will be ignored.
508  * @end_date: Search hits after this date will be ignored.
509  * @min_size: Messagers smaller then this size will be ingored.
510  * @flags: A list of flags where to search so the documentation 
511  * of %ModestDBusSearchFlags for details.
512  * @hits: A pointer to a valid GList pointer that will contain the search
513  * hits (ModestSearchHit). The list and the items must be freed by the caller 
514  * with modest_search_hit_list_free().
515  *
516  * This method will search the folder specified by a valid url in @folder or all
517  * known accounts (local and remote) if %NULL for matches of the search term(s)
518  * specified in @query. It is legal to specify 0 in @start_date, @end_date and
519  * @min_size to ignore these parameters during the search otherwise those message
520  * that do not meet the specifed dates or size will be ignored.
521  * Where to search, be it subject, sender or the whole body can be specified by
522  * the @flags parameter.
523  *
524  * Upon success TRUE is returned and @hits will include the search hits or the list
525  * migh be empty if none of the messages matched the search criteria. The returned
526  * list must be freed with modest_search_hit_list_free (). It is save to pass
527  * %NULL to this function so you can call this function on the result list no matter
528  * if a hit was found or not (means the list is empty - i.e. %NULL)
529  * FALSE will only be return if an error during the remote procedure call (rpc) 
530  * occured or if the specified folder could not be found.
531  *
532  * NOTE: The body of a message can only be searched if it was previously downloaded by
533  * modest. This function does also not attempt do to remote searches (i.e. IMAP search).
534  *
535  * Example to search every account for message containing "no":
536  * <informalexample><programlisting>
537  * ModestDBusSearchFlags  flags;
538  * osso_context_t        *osso_context;
539  * GList                 *hits;
540  * GList                 *iter;
541  * gboolean               res;
542  * 
543  * [...] Initialize osso context [...]
544  *
545  * res = libmodest_dbus_client_search (osso_context,
546  *                                     "no",
547  *                                     NULL,
548  *                                     0,
549  *                                     0,
550  *                                     0,
551  *                                     flags,
552  *                                     &hits);
553  * 
554  * for (iter = hits; iter; iter = iter->next) {
555  *      ModestSearchHit *hit = (ModestSearchHit *) iter->data;
556  *      
557  *      [...] Do something with the hit [...]
558  *
559  *      }
560  *
561  *      modest_search_hit_list_free (hits);
562  * </programlisting></informalexample>
563  * 
564  * Return value: TRUE if the search succeded or FALSE for an error during the search
565  **/
566 gboolean
567 libmodest_dbus_client_search (osso_context_t          *osso_ctx,
568                               const gchar             *query,
569                               const gchar             *folder,
570                               time_t                   start_date,
571                               time_t                   end_date,
572                               guint32                  min_size,
573                               ModestDBusSearchFlags    flags,
574                               GList                  **hits)
575 {
576
577         DBusMessage *msg;
578         dbus_bool_t res;
579         DBusConnection *con;
580         DBusMessageIter iter;
581         DBusMessageIter child;
582         DBusMessage *reply = NULL;
583         gint timeout;
584         int arg_type;
585         dbus_int64_t sd_v;
586         dbus_int64_t ed_v;
587         dbus_int32_t flags_v;
588         dbus_uint32_t size_v;
589
590         if (query == NULL) {
591                 return FALSE;
592         }
593
594         con = osso_get_dbus_connection (osso_ctx);
595
596         if (con == NULL) {
597                 g_warning ("Could not get dbus connection\n");
598                 return FALSE;
599
600         }
601
602
603         msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
604                 MODEST_DBUS_OBJECT,
605                 MODEST_DBUS_IFACE,
606                 MODEST_DBUS_METHOD_SEARCH);
607
608     if (msg == NULL) {
609         //ULOG_ERR_F("dbus_message_new_method_call failed");
610                 return OSSO_ERROR;
611     }
612
613         if (folder == NULL) {
614                 folder = "";
615         }
616
617         sd_v = start_date;
618         ed_v = end_date;
619         flags_v = (dbus_int32_t) flags;
620         size_v = (dbus_uint32_t) min_size;
621
622         res  = dbus_message_append_args (msg,
623                                          DBUS_TYPE_STRING, &query,
624                                          DBUS_TYPE_STRING, &folder,
625                                          DBUS_TYPE_INT64, &sd_v,
626                                          DBUS_TYPE_INT64, &ed_v,
627                                          DBUS_TYPE_INT32, &flags_v,
628                                          DBUS_TYPE_UINT32, &size_v,
629                                          DBUS_TYPE_INVALID);
630
631         dbus_message_set_auto_start (msg, TRUE);
632
633         /* Use a long timeout (2 minutes) because the search currently 
634          * gets folders and messages from the servers. */
635         timeout = 120000; //milliseconds.
636         //osso_rpc_get_timeout (osso_ctx, &timeout);
637
638     /*printf("DEBUG: %s: Before dbus_connection_send_with_reply_and_block().\n", 
639                 __FUNCTION__); */
640         /* TODO: Detect the timeout somehow. */
641         DBusError err;
642         dbus_error_init (&err);
643         reply = dbus_connection_send_with_reply_and_block (con,
644                                                            msg, 
645                                                            timeout,
646                                                            &err);
647         /* printf("DEBUG: %s: dbus_connection_send_with_reply_and_block() finished.\n", 
648                 __FUNCTION__); */
649
650         dbus_message_unref (msg);
651
652         if (!reply) {
653                 g_warning("%s: dbus_connection_send_with_reply_and_block() error: %s", 
654                         __FUNCTION__, err.message);
655                 return FALSE;
656         }
657
658         switch (dbus_message_get_type (reply)) {
659
660                 case DBUS_MESSAGE_TYPE_ERROR:
661                         dbus_set_error_from_message (&err, reply);
662                         //XXX to GError?!
663                         dbus_error_free (&err);
664                         dbus_message_unref (reply);
665                         return FALSE;
666
667                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
668                         /* ok we are good to go
669                          * lets drop outa here and handle that */
670                         break;
671                 default:
672                         //ULOG_WARN_F("got unknown message type as reply");
673                         //retval->type = DBUS_TYPE_STRING;
674                         //retval->value.s = g_strdup("Invalid return value");
675                         //XXX to GError?! 
676                         dbus_message_unref (reply);
677                         return FALSE;
678         }
679
680         g_debug ("%s: message return", __FUNCTION__);
681
682         dbus_message_iter_init (reply, &iter);
683         arg_type = dbus_message_iter_get_arg_type (&iter);
684         
685         dbus_message_iter_recurse (&iter, &child);
686         *hits = NULL;
687
688         do {
689                 ModestSearchHit *hit;
690
691                 hit = modest_dbus_message_iter_get_search_hit (&child);
692
693                 if (hit) {
694                         *hits = g_list_prepend (*hits, hit);    
695                 }
696
697         } while (dbus_message_iter_next (&child));
698
699         dbus_message_unref (reply);
700
701
702         /* TODO: This is from osso source, do we need it? */
703 #if 0
704         /* Tell TaskNavigator to show "launch banner" */
705         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
706                                             APP_LAUNCH_BANNER_METHOD_PATH,
707                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
708                                             APP_LAUNCH_BANNER_METHOD);
709
710         if (msg == NULL) {
711                 g_warn ("dbus_message_new_method_call failed");
712         }
713
714
715
716         dbus_message_append_args (msg,
717                                   DBUS_TYPE_STRING,
718                                   &service,
719                                   DBUS_TYPE_INVALID);
720
721         b = dbus_connection_send (conn, msg, NULL);
722
723         if (b == NULL) {
724                 ULOG_WARN_F("dbus_connection_send failed");
725         }
726
727         dbus_message_unref (msg);
728 #endif
729
730         return TRUE;
731 }
732
733
734 static void
735 modest_folder_result_free (ModestFolderResult *item)
736 {
737         g_free (item->folder_name);
738         g_free (item->folder_uri);
739         g_slice_free (ModestFolderResult, item);
740 }
741
742 void
743 modest_folder_result_list_free (GList *list)
744 {
745         GList *iter;
746
747         if (list == NULL) {
748                 return;
749         }
750
751         for (iter = list; iter; iter = iter->next) {
752                 modest_folder_result_free ((ModestFolderResult *) iter->data);
753         }
754
755         g_list_free (list);
756 }
757
758
759 /** Get the values from the complex type (GET_FOLDERS_RESULT_DBUS_TYPE)
760  * in the D-Bus return message. */
761 static ModestFolderResult *
762 modest_dbus_message_iter_get_folder_item (DBusMessageIter *parent)
763 {
764         gboolean error = FALSE;
765         ModestFolderResult *item = g_slice_new0 (ModestFolderResult);
766
767         int arg_type = dbus_message_iter_get_arg_type (parent);
768
769         if (arg_type != 'r') {
770                 return NULL;
771         }
772
773         DBusMessageIter  child;
774         dbus_message_iter_recurse (parent, &child);
775         
776         /* folder name: */
777         arg_type = dbus_message_iter_get_arg_type (&child);
778
779         if (arg_type != DBUS_TYPE_STRING) {
780                 error = TRUE;
781                 goto out;
782         }
783
784         item->folder_name = _dbus_iter_get_string_or_null (&child);
785         
786         
787         dbus_bool_t res = dbus_message_iter_next (&child);
788         if (res == FALSE) {
789                 error = TRUE;
790                 goto out;
791         }
792
793         /* folder URI:  */
794         arg_type = dbus_message_iter_get_arg_type (&child);
795
796         if (arg_type != DBUS_TYPE_STRING) {
797                 error = TRUE;
798                 goto out;
799         }
800
801         item->folder_uri = _dbus_iter_get_string_or_null (&child);
802
803
804 out:
805         if (error) {
806                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
807                 modest_folder_result_free (item);
808                 item = NULL;
809         }
810
811         return item;
812 }
813
814 /**
815  * libmodest_dbus_client_get_folders:
816  * @osso_ctx: A valid #osso_context_t object.
817  * @folders: A pointer to a valid GList pointer that will contain the folder items
818  * (ModestFolderResult). The list and the items must be freed by the caller 
819  * with modest_folder_result_list_free().
820  *
821  * This method will obtain a list of folders in the default account.
822  *
823  * Upon success TRUE is returned and @folders will include the folders or the list
824  * might be empty if there are no folders. The returned
825  * list must be freed with modest_folder_result_list_free ().
826  *
827  * NOTE: A folder will only be retrieved if it was previously downloaded by
828  * modest. This function does also not attempt do to remote refreshes (i.e. IMAP).
829  * 
830  * Return value: TRUE if the request succeded or FALSE for an error.
831  **/
832 gboolean
833 libmodest_dbus_client_get_folders (osso_context_t          *osso_ctx,
834                               GList                  **folders)
835 {
836         /* Initialize output argument: */
837         if (folders)
838                 *folders = NULL;
839         else
840                 return FALSE;
841
842         DBusConnection *con = osso_get_dbus_connection (osso_ctx);
843
844         if (con == NULL) {
845                 g_warning ("Could not get dbus connection\n");
846                 return FALSE;
847
848         }
849
850         DBusMessage *msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
851                 MODEST_DBUS_OBJECT,
852                 MODEST_DBUS_IFACE,
853                 MODEST_DBUS_METHOD_GET_FOLDERS);
854
855     if (msg == NULL) {
856         //ULOG_ERR_F("dbus_message_new_method_call failed");
857                 return OSSO_ERROR;
858     }
859
860         dbus_message_set_auto_start (msg, TRUE);
861
862         /* Use a long timeout (2 minutes) because the search currently 
863          * gets folders from the servers. */
864         gint timeout = 120000;
865         //osso_rpc_get_timeout (osso_ctx, &timeout);
866
867         DBusError err;
868         dbus_error_init (&err);
869         DBusMessage *reply = dbus_connection_send_with_reply_and_block (con,
870                                                            msg, 
871                                                            timeout,
872                                                            &err);
873
874         dbus_message_unref (msg);
875         msg = NULL;
876
877         if (reply == NULL) {
878                 g_warning("%s: dbus_connection_send_with_reply_and_block() error:\n   %s", 
879                         __FUNCTION__, err.message);
880                 return FALSE;
881         }
882
883         switch (dbus_message_get_type (reply)) {
884
885                 case DBUS_MESSAGE_TYPE_ERROR:
886                         dbus_set_error_from_message (&err, reply);
887                         //XXX to GError?!
888                         dbus_error_free (&err);
889                         dbus_message_unref (reply);
890                         return FALSE;
891
892                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
893                         /* ok we are good to go
894                          * lets drop outa here and handle that */
895                         break;
896                 default:
897                         //ULOG_WARN_F("got unknown message type as reply");
898                         //retval->type = DBUS_TYPE_STRING;
899                         //retval->value.s = g_strdup("Invalid return value");
900                         //XXX to GError?! 
901                         dbus_message_unref (reply);
902                         return FALSE;
903         }
904
905         g_debug ("%s: message return", __FUNCTION__);
906
907         DBusMessageIter iter;
908         dbus_message_iter_init (reply, &iter);
909         /* int arg_type = dbus_message_iter_get_arg_type (&iter); */
910         
911         DBusMessageIter child;
912         dbus_message_iter_recurse (&iter, &child);
913
914         do {
915                 ModestFolderResult *item = modest_dbus_message_iter_get_folder_item (&child);
916
917                 if (item) {
918                         *folders = g_list_append (*folders, item);      
919                 }
920
921         } while (dbus_message_iter_next (&child));
922
923         dbus_message_unref (reply);
924
925
926         /* TODO: This is from osso source, do we need it? */
927 #if 0
928         /* Tell TaskNavigator to show "launch banner" */
929         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
930                                             APP_LAUNCH_BANNER_METHOD_PATH,
931                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
932                                             APP_LAUNCH_BANNER_METHOD);
933
934         if (msg == NULL) {
935                 g_warn ("dbus_message_new_method_call failed");
936         }
937
938
939
940         dbus_message_append_args (msg,
941                                   DBUS_TYPE_STRING,
942                                   &service,
943                                   DBUS_TYPE_INVALID);
944
945         b = dbus_connection_send (conn, msg, NULL);
946
947         if (b == NULL) {
948                 ULOG_WARN_F("dbus_connection_send failed");
949         }
950
951         dbus_message_unref (msg);
952 #endif
953
954         return TRUE;
955 }
956
957