Add test for dbus get_unread_messages method.
[modest] / libmodest-dbus-client / src / 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 "libmodest-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 *escaped;
55                         gchar *tmp;
56                         escaped = g_uri_escape_string ((const gchar *) (iter->data), NULL, TRUE);
57                         tmp = g_strconcat(attachments_str, ",", escaped, NULL);
58                         g_free(escaped);
59                         g_free(attachments_str);
60                         attachments_str = tmp;
61                 }
62                 iter = g_slist_next(iter);
63         }
64         return attachments_str;
65 }
66
67 /**
68  * libmodest_dbus_client_mail_to:
69  * @osso_context: a valid #osso_context_t object.
70  * @mailto_uri: A mailto URI.
71  * 
72  * This function will try to do a remote procedure call (rpc)
73  * into modest (or start it if necessary) and open a composer
74  * window with the supplied parameters prefilled.
75  *
76  * Return value: Whether or not the rpc call to modest
77  * was successfull
78  **/
79 gboolean 
80 libmodest_dbus_client_mail_to (osso_context_t *osso_context, const gchar *mailto_uri)
81 {
82         osso_rpc_t retval = { 0 };
83         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
84                    MODEST_DBUS_NAME, 
85                    MODEST_DBUS_METHOD_MAIL_TO, &retval, 
86                    DBUS_TYPE_STRING, mailto_uri, 
87                    DBUS_TYPE_INVALID);
88                 
89         if (ret != OSSO_OK) {
90                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
91                 return FALSE;
92         } else {
93                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
94         }
95         
96         osso_rpc_free_val(&retval);
97         
98         return TRUE;
99 }
100
101 /**
102  * libmodest_dbus_client_compose_mail:
103  * @osso_context: a valid #osso_context_t object.
104  * @to: The Recipients (From: line)
105  * @cc: Recipients for carbon copies
106  * @bcc: Recipients for blind carbon copies
107  * @subject: Subject line
108  * @body: The actual body of the mail to compose.
109  * @attachments: Additional list of attachments. A list of URI strings.
110  * 
111  * This function will try to do a remote procedure call (rpc)
112  * into modest (or start it if necessary) and open a composer
113  * window with the supplied parameters prefilled.
114  *
115  * Return value: Whether or not the rpc call to modest
116  * was successfull
117  **/
118 gboolean
119 libmodest_dbus_client_compose_mail (osso_context_t *osso_context, const gchar *to, const gchar *cc, 
120         const gchar *bcc, const gchar* subject, const gchar* body, GSList *attachments)
121 {
122         osso_rpc_t retval = { 0 };
123
124         gchar *attachments_str = get_attachments_string(attachments);
125
126         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context,
127                    MODEST_DBUS_NAME, 
128                    MODEST_DBUS_METHOD_COMPOSE_MAIL, &retval, 
129                    DBUS_TYPE_STRING, to, 
130                    DBUS_TYPE_STRING, cc, 
131                    DBUS_TYPE_STRING, bcc, 
132                    DBUS_TYPE_STRING, subject, 
133                    DBUS_TYPE_STRING, body,
134                    DBUS_TYPE_STRING, attachments_str,
135                    DBUS_TYPE_INVALID);
136
137         g_free (attachments_str);
138
139         if (ret != OSSO_OK) {
140                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
141                 return FALSE;
142         } else {
143                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
144         }
145
146         osso_rpc_free_val(&retval);
147
148
149         return TRUE;
150 }
151
152 /**
153  * libmodest_dbus_client_open_message:
154  * @osso_context: a valid #osso_context_t object.
155  * @msg_uri: A valid url to a mail
156  *
157  * This method will try to find the message supplied
158  * by @msg_uri and open it for display if found. 
159  * It will use remote procedure calls (rpc) over 
160  * dbus to do so.
161  *  
162  * Return value: TRUE on successs, FALSE on error
163  **/
164 gboolean 
165 libmodest_dbus_client_open_message (osso_context_t *osso_context, const gchar *mail_uri)
166 {
167         osso_rpc_t retval = { 0 };
168         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
169                    MODEST_DBUS_NAME, 
170                    MODEST_DBUS_METHOD_OPEN_MESSAGE, &retval, 
171                    DBUS_TYPE_STRING, mail_uri, 
172                    DBUS_TYPE_INVALID);
173                 
174         if (ret != OSSO_OK) {
175                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
176                 return FALSE;
177         } else {
178                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
179         }
180         
181         osso_rpc_free_val(&retval);
182         
183         return TRUE;
184 }
185
186 gboolean 
187 libmodest_dbus_client_send_and_receive (osso_context_t *osso_context)
188 {
189         osso_rpc_t retval = { 0 };
190         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
191                    MODEST_DBUS_NAME, 
192                    MODEST_DBUS_METHOD_SEND_RECEIVE, &retval, 
193                    DBUS_TYPE_INVALID);
194                 
195         if (ret != OSSO_OK) {
196                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
197                 return FALSE;
198         } else {
199                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
200         }
201         
202         osso_rpc_free_val(&retval);
203         
204         return TRUE;
205 }
206
207 gboolean 
208 libmodest_dbus_client_send_and_receive_full (osso_context_t *osso_context, 
209                                              const gchar *account, 
210                                              gboolean manual)
211 {
212         osso_rpc_t retval = { 0 };
213         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
214                                                              MODEST_DBUS_NAME,
215                                                              MODEST_DBUS_METHOD_SEND_RECEIVE_FULL, &retval,
216                                                              DBUS_TYPE_STRING, account,
217                                                              DBUS_TYPE_BOOLEAN, manual,
218                                                              DBUS_TYPE_INVALID);
219                 
220         if (ret != OSSO_OK) {
221                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
222                 return FALSE;
223         } else {
224                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
225         }
226         
227         osso_rpc_free_val(&retval);
228         
229         return TRUE;
230 }
231
232 gboolean 
233 libmodest_dbus_client_update_folder_counts (osso_context_t *osso_context, 
234                                             const gchar *account)
235 {
236         osso_rpc_t retval = { 0 };
237         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
238                                                              MODEST_DBUS_NAME,
239                                                              MODEST_DBUS_METHOD_UPDATE_FOLDER_COUNTS, &retval,
240                                                              DBUS_TYPE_STRING, account,
241                                                              DBUS_TYPE_INVALID);
242                 
243         if (ret != OSSO_OK) {
244                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
245                 return FALSE;
246         } else {
247                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
248         }
249         
250         osso_rpc_free_val(&retval);
251         
252         return TRUE;
253 }
254
255 gboolean 
256 libmodest_dbus_client_open_default_inbox (osso_context_t *osso_context)
257 {
258         osso_rpc_t retval = { 0 };
259         const osso_return_t ret = osso_rpc_run_with_defaults(osso_context, 
260                    MODEST_DBUS_NAME, 
261                    MODEST_DBUS_METHOD_OPEN_DEFAULT_INBOX, &retval, 
262                    DBUS_TYPE_INVALID);
263                 
264         if (ret != OSSO_OK) {
265                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
266                 return FALSE;
267         } else {
268                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
269         }
270         
271         osso_rpc_free_val(&retval);
272         
273         return TRUE;
274 }
275
276 gboolean
277 libmodest_dbus_client_open_account (osso_context_t *osso_context,
278                                     const gchar *account_id)
279 {
280         osso_rpc_t retval = { 0 };
281         const osso_return_t ret =
282                 osso_rpc_run_with_defaults(osso_context,
283                                            MODEST_DBUS_NAME,
284                                            MODEST_DBUS_METHOD_OPEN_ACCOUNT, &retval,
285                                            DBUS_TYPE_STRING, account_id,
286                                            DBUS_TYPE_INVALID);
287
288         if (ret != OSSO_OK) {
289                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
290                 return FALSE;
291         } else {
292                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
293         }
294
295         osso_rpc_free_val(&retval);
296
297         return TRUE;
298 }
299
300 gboolean
301 libmodest_dbus_client_open_edit_accounts_dialog (osso_context_t *osso_context)
302 {
303         osso_rpc_t retval = { 0 };
304         const osso_return_t ret =
305                 osso_rpc_run_with_defaults(osso_context,
306                                            MODEST_DBUS_NAME,
307                                            MODEST_DBUS_METHOD_OPEN_EDIT_ACCOUNTS_DIALOG, &retval,
308                                            DBUS_TYPE_INVALID);
309
310         if (ret != OSSO_OK) {
311                 printf("debug: %s: osso_rpc_run() failed.\n", __FUNCTION__);
312                 return FALSE;
313         } else {
314                 printf("debug: %s: osso_rpc_run() succeeded.\n", __FUNCTION__);
315         }
316
317         osso_rpc_free_val(&retval);
318
319         return TRUE;
320 }
321
322 /**
323  * libmodest_dbus_client_delete_message:
324  * @osso_context: a valid #osso_context_t object.
325  * @msg_uri: A valid url to a mail 
326  *
327  * This method will try to find the message supplied
328  * by @msg_uri and if found delete it. It will use
329  * remote procedure calls (rpc) over dbus to do so.
330  * 
331  * Return value: TRUE on successs, FALSE on error
332  **/
333 gboolean
334 libmodest_dbus_client_delete_message (osso_context_t   *osso_ctx,
335                                       const char       *msg_uri)
336 {
337         osso_rpc_t    retval = { 0 };
338         osso_return_t ret;
339        
340         ret = osso_rpc_run_with_defaults (osso_ctx, 
341                                           MODEST_DBUS_NAME, 
342                                           MODEST_DBUS_METHOD_DELETE_MESSAGE, &retval, 
343                                           DBUS_TYPE_STRING, msg_uri, 
344                                           DBUS_TYPE_INVALID);
345                 
346         if (ret != OSSO_OK) {
347                 g_debug ("debug: osso_rpc_run() failed.\n");
348         } else {
349                 g_debug ("debug: osso_rpc_run() succeeded.\n");
350         }
351         
352         osso_rpc_free_val (&retval);
353
354         return ret == OSSO_OK;
355 }
356
357 static void
358 modest_search_hit_free (ModestSearchHit *hit)
359 {
360         g_free (hit->msgid);
361         g_slice_free (ModestSearchHit, hit);
362 }
363
364 void
365 modest_search_hit_list_free (GList *hits)
366 {
367         GList *iter;
368
369         if (hits == NULL) {
370                 return;
371         }
372
373         for (iter = hits; iter; iter = iter->next) {
374                 modest_search_hit_free ((ModestSearchHit *) iter->data);
375         }
376
377         g_list_free (hits);
378 }
379
380 static void
381 modest_account_hits_hits_list_free (GList *account_hits_hits_list)
382 {
383         GList *iter;
384
385         if (account_hits_hits_list == NULL) {
386                 return;
387         }
388
389         for (iter = account_hits_hits_list; iter; iter = iter->next) {
390                 ModestGetUnreadMessagesHit *hit = (ModestGetUnreadMessagesHit *) iter->data;
391                 g_free (hit->subject);
392                 g_slice_free (ModestGetUnreadMessagesHit, hit);
393         }
394
395         g_list_free (account_hits_hits_list);
396 }
397
398 static void
399 modest_account_hits_free (ModestAccountHits *account_hits)
400 {
401         g_free (account_hits->account_id);
402         g_free (account_hits->account_name);
403         modest_account_hits_hits_list_free (account_hits->hits);
404         g_slice_free (ModestAccountHits, account_hits);
405 }
406
407 void
408 modest_account_hits_list_free (GList *account_hits_list)
409 {
410         GList *iter;
411
412         if (account_hits_list == NULL) {
413                 return;
414         }
415
416         for (iter = account_hits_list; iter; iter = iter->next) {
417                 modest_account_hits_free ((ModestAccountHits *) iter->data);
418         }
419
420         g_list_free (account_hits_list);
421 }
422
423 static char *
424 _dbus_iter_get_string_or_null (DBusMessageIter *iter)
425 {
426         const char *string = NULL;
427         char       *ret = NULL;
428
429         dbus_message_iter_get_basic (iter, &string);
430         
431         if (string && strlen (string)) {
432                 ret = g_strdup (string);
433         }
434
435         return ret;
436 }
437
438 static guint64
439 _dbus_iter_get_uint64 (DBusMessageIter *iter)
440 {
441         dbus_uint64_t ui64v;
442         guint64       ret;
443
444         ui64v = 0;
445         dbus_message_iter_get_basic (iter, &ui64v);
446
447         ret = (guint64) ui64v;
448
449         return ret;
450 }
451
452
453 static gint64
454 _dbus_iter_get_int64 (DBusMessageIter *iter)
455 {
456         dbus_int64_t i64v;
457         gint64       ret;
458
459         i64v = 0;
460         dbus_message_iter_get_basic (iter, &i64v);
461
462         ret = (gint64) i64v;
463
464         return ret;
465 }
466
467 static gboolean
468 _dbus_iter_get_boolean (DBusMessageIter *iter)
469
470 {
471         dbus_bool_t  val;
472         gboolean     ret;
473
474         val = FALSE;
475         dbus_message_iter_get_basic (iter, &val);
476
477         ret = (gboolean) val;
478
479         return ret;
480 }
481
482 /** Get the values from the complex type (SEARCH_HIT_DBUS_TYPE)
483  * in the D-Bus return message. */
484 static ModestSearchHit *
485 modest_dbus_message_iter_get_search_hit (DBusMessageIter *parent)
486 {
487         ModestSearchHit *hit;
488         DBusMessageIter  child;
489         dbus_bool_t      res;
490         int              arg_type;
491         gboolean         error;
492
493         error = FALSE;
494         hit = g_slice_new0 (ModestSearchHit);
495
496         arg_type = dbus_message_iter_get_arg_type (parent);
497
498         if (arg_type != 'r') {
499                 return NULL;
500         }
501
502         dbus_message_iter_recurse (parent, &child);
503         
504         /* msgid  */
505         arg_type = dbus_message_iter_get_arg_type (&child);
506
507         if (arg_type != DBUS_TYPE_STRING) {
508                 error = TRUE;
509                 goto out;
510         }
511
512         hit->msgid = _dbus_iter_get_string_or_null (&child);
513
514         res = dbus_message_iter_next (&child);
515         if (res == FALSE) {
516                 error = TRUE;
517                 goto out;
518         }
519
520         /* subject  */
521         arg_type = dbus_message_iter_get_arg_type (&child);
522
523         if (arg_type != DBUS_TYPE_STRING) {
524                 error = TRUE;
525                 goto out;
526         }
527
528         hit->subject = _dbus_iter_get_string_or_null (&child);
529
530         res = dbus_message_iter_next (&child);
531         if (res == FALSE) {
532                 error = TRUE;
533                 goto out;
534         }
535
536         /* folder  */
537         arg_type = dbus_message_iter_get_arg_type (&child);
538
539         if (arg_type != DBUS_TYPE_STRING) {
540                 error = TRUE;
541                 goto out;
542         }
543
544         hit->folder = _dbus_iter_get_string_or_null (&child);
545
546         res = dbus_message_iter_next (&child);
547         if (res == FALSE) {
548                 error = TRUE;
549                 goto out;
550         }
551
552         /* sender  */
553         arg_type = dbus_message_iter_get_arg_type (&child);
554
555         if (arg_type != DBUS_TYPE_STRING) {
556                 error = TRUE;
557                 goto out;
558         }
559
560         hit->sender = _dbus_iter_get_string_or_null (&child);
561
562         res = dbus_message_iter_next (&child);
563         if (res == FALSE) {
564                 error = TRUE;
565                 goto out;
566         }
567
568         /* msize  */
569         arg_type = dbus_message_iter_get_arg_type (&child);
570
571         if (arg_type != DBUS_TYPE_UINT64) {
572                 error = TRUE;
573                 goto out;
574         }
575
576         hit->msize = _dbus_iter_get_uint64 (&child);
577
578         res = dbus_message_iter_next (&child);
579         if (res == FALSE) {
580                 error = TRUE;
581                 goto out;
582         }
583
584         /* has_attachment  */
585         arg_type = dbus_message_iter_get_arg_type (&child);
586
587         if (arg_type != DBUS_TYPE_BOOLEAN) {
588                 error = TRUE;
589                 goto out;
590         }
591
592         hit->has_attachment = _dbus_iter_get_boolean (&child); 
593
594         res = dbus_message_iter_next (&child);
595         if (res == FALSE) {
596                 error = TRUE;
597                 goto out;
598         }
599
600         /* is_unread  */
601         arg_type = dbus_message_iter_get_arg_type (&child);
602
603         if (arg_type != DBUS_TYPE_BOOLEAN) {
604                 error = TRUE;
605                 goto out;
606         }
607
608         hit->is_unread = _dbus_iter_get_boolean (&child);  
609
610         res = dbus_message_iter_next (&child);
611         if (res == FALSE) {
612                 error = TRUE;
613                 goto out;
614         }
615
616         /* timestamp  */
617         arg_type = dbus_message_iter_get_arg_type (&child);
618
619         if (arg_type != DBUS_TYPE_INT64) {
620                 error = TRUE;
621                 goto out;
622         }
623
624         hit->timestamp = _dbus_iter_get_int64 (&child); 
625
626         res = dbus_message_iter_next (&child);
627         if (res == TRUE) {
628                 error = TRUE;
629                 goto out;
630         }       
631
632 out:
633         if (error) {
634                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
635                 modest_search_hit_free (hit);
636                 hit = NULL;
637         }
638
639         return hit;
640 }
641
642 static ModestGetUnreadMessagesHit *
643 modest_dbus_message_iter_get_unread_messages_hit (DBusMessageIter *parent)
644 {
645         ModestGetUnreadMessagesHit *hit;
646         DBusMessageIter  child;
647         dbus_bool_t      res;
648         int              arg_type;
649         gboolean         error;
650
651         error = FALSE;
652
653         arg_type = dbus_message_iter_get_arg_type (parent);
654
655         if (arg_type != 'r') {
656                 return NULL;
657         }
658
659         hit = g_slice_new0 (ModestGetUnreadMessagesHit);
660         dbus_message_iter_recurse (parent, &child);
661         
662         /* timestamp  */
663         arg_type = dbus_message_iter_get_arg_type (&child);
664
665         if (arg_type != DBUS_TYPE_INT64) {
666                 error = TRUE;
667                 goto out;
668         }
669
670         hit->timestamp = _dbus_iter_get_int64 (&child); 
671
672         res = dbus_message_iter_next (&child);
673         if (res == FALSE) {
674                 error = TRUE;
675                 goto out;
676         }       
677
678         /* subject  */
679         arg_type = dbus_message_iter_get_arg_type (&child);
680
681         if (arg_type != DBUS_TYPE_STRING) {
682                 error = TRUE;
683                 goto out;
684         }
685
686         hit->subject = _dbus_iter_get_string_or_null (&child);
687
688         res = dbus_message_iter_next (&child);
689         if (res == TRUE) {
690                 error = TRUE;
691                 goto out;
692         }
693
694 out:
695         if (error) {
696                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
697                 g_slice_free (ModestGetUnreadMessagesHit, hit);
698                 hit = NULL;
699         }
700
701         return hit;
702 }
703
704 /**
705  * libmodest_dbus_client_search:
706  * @osso_ctx: A valid #osso_context_t object.
707  * @query: The term to search for.
708  * @folder: An url to specific folder or %NULL to search everywhere.
709  * @start_date: Search hits before this date will be ignored.
710  * @end_date: Search hits after this date will be ignored.
711  * @min_size: Messagers smaller then this size will be ingored.
712  * @flags: A list of flags where to search so the documentation 
713  * of %ModestDBusSearchFlags for details.
714  * @hits: A pointer to a valid GList pointer that will contain the search
715  * hits (ModestSearchHit). The list and the items must be freed by the caller 
716  * with modest_search_hit_list_free().
717  *
718  * This method will search the folder specified by a valid url in @folder or all
719  * known accounts (local and remote) if %NULL for matches of the search term(s)
720  * specified in @query. It is legal to specify 0 in @start_date, @end_date and
721  * @min_size to ignore these parameters during the search otherwise those message
722  * that do not meet the specifed dates or size will be ignored.
723  * Where to search, be it subject, sender or the whole body can be specified by
724  * the @flags parameter.
725  *
726  * Upon success TRUE is returned and @hits will include the search hits or the list
727  * migh be empty if none of the messages matched the search criteria. The returned
728  * list must be freed with modest_search_hit_list_free (). It is save to pass
729  * %NULL to this function so you can call this function on the result list no matter
730  * if a hit was found or not (means the list is empty - i.e. %NULL)
731  * FALSE will only be return if an error during the remote procedure call (rpc) 
732  * occured or if the specified folder could not be found.
733  *
734  * NOTE: The body of a message can only be searched if it was previously downloaded by
735  * modest. This function does also not attempt do to remote searches (i.e. IMAP search).
736  *
737  * Example to search every account for message containing "no":
738  * <informalexample><programlisting>
739  * ModestDBusSearchFlags  flags;
740  * osso_context_t        *osso_context;
741  * GList                 *hits;
742  * GList                 *iter;
743  * gboolean               res;
744  * 
745  * [...] Initialize osso context [...]
746  *
747  * res = libmodest_dbus_client_search (osso_context,
748  *                                     "no",
749  *                                     NULL,
750  *                                     0,
751  *                                     0,
752  *                                     0,
753  *                                     flags,
754  *                                     &hits);
755  * 
756  * for (iter = hits; iter; iter = iter->next) {
757  *      ModestSearchHit *hit = (ModestSearchHit *) iter->data;
758  *      
759  *      [...] Do something with the hit [...]
760  *
761  *      }
762  *
763  *      modest_search_hit_list_free (hits);
764  * </programlisting></informalexample>
765  * 
766  * Return value: TRUE if the search succeded or FALSE for an error during the search
767  **/
768 gboolean
769 libmodest_dbus_client_search (osso_context_t          *osso_ctx,
770                               const gchar             *query,
771                               const gchar             *folder,
772                               time_t                   start_date,
773                               time_t                   end_date,
774                               guint32                  min_size,
775                               ModestDBusSearchFlags    flags,
776                               GList                  **hits)
777 {
778
779         DBusMessage *msg;
780         dbus_bool_t res;
781         DBusConnection *con;
782         DBusMessageIter iter;
783         DBusMessageIter child;
784         DBusMessage *reply = NULL;
785         gint timeout;
786         int arg_type;
787         dbus_int64_t sd_v;
788         dbus_int64_t ed_v;
789         dbus_int32_t flags_v;
790         dbus_uint32_t size_v;
791
792         if (query == NULL) {
793                 return FALSE;
794         }
795
796         con = osso_get_dbus_connection (osso_ctx);
797
798         if (con == NULL) {
799                 g_warning ("Could not get dbus connection\n");
800                 return FALSE;
801
802         }
803
804
805         msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
806                 MODEST_DBUS_OBJECT,
807                 MODEST_DBUS_IFACE,
808                 MODEST_DBUS_METHOD_SEARCH);
809
810     if (msg == NULL) {
811         //ULOG_ERR_F("dbus_message_new_method_call failed");
812                 return OSSO_ERROR;
813     }
814
815         if (folder == NULL) {
816                 folder = "";
817         }
818
819         sd_v = (dbus_int64_t) start_date;
820         ed_v = (dbus_int64_t) end_date;
821         flags_v = (dbus_int32_t) flags;
822         size_v = (dbus_uint32_t) min_size;
823
824         res  = dbus_message_append_args (msg,
825                                          DBUS_TYPE_STRING, &query,
826                                          DBUS_TYPE_STRING, &folder,
827                                          DBUS_TYPE_INT64, &sd_v,
828                                          DBUS_TYPE_INT64, &ed_v,
829                                          DBUS_TYPE_INT32, &flags_v,
830                                          DBUS_TYPE_UINT32, &size_v,
831                                          DBUS_TYPE_INVALID);
832
833         dbus_message_set_auto_start (msg, TRUE);
834
835         /* Use a long timeout (2 minutes) because the search currently 
836          * gets folders and messages from the servers. */
837         timeout = 120000; //milliseconds.
838         //osso_rpc_get_timeout (osso_ctx, &timeout);
839
840     /*printf("DEBUG: %s: Before dbus_connection_send_with_reply_and_block().\n", 
841                 __FUNCTION__); */
842         /* TODO: Detect the timeout somehow. */
843         DBusError err;
844         dbus_error_init (&err);
845         reply = dbus_connection_send_with_reply_and_block (con,
846                                                            msg, 
847                                                            timeout,
848                                                            &err);
849         /* printf("DEBUG: %s: dbus_connection_send_with_reply_and_block() finished.\n", 
850                 __FUNCTION__); */
851
852         dbus_message_unref (msg);
853
854         if (!reply) {
855                 g_warning("%s: dbus_connection_send_with_reply_and_block() error: %s", 
856                         __FUNCTION__, err.message);
857                 return FALSE;
858         }
859
860         switch (dbus_message_get_type (reply)) {
861
862                 case DBUS_MESSAGE_TYPE_ERROR:
863                         dbus_set_error_from_message (&err, reply);
864                         //XXX to GError?!
865                         dbus_error_free (&err);
866                         dbus_message_unref (reply);
867                         return FALSE;
868
869                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
870                         /* ok we are good to go
871                          * lets drop outa here and handle that */
872                         break;
873                 default:
874                         //ULOG_WARN_F("got unknown message type as reply");
875                         //retval->type = DBUS_TYPE_STRING;
876                         //retval->value.s = g_strdup("Invalid return value");
877                         //XXX to GError?! 
878                         dbus_message_unref (reply);
879                         return FALSE;
880         }
881
882         g_debug ("%s: message return", __FUNCTION__);
883
884         dbus_message_iter_init (reply, &iter);
885         arg_type = dbus_message_iter_get_arg_type (&iter);
886         
887         dbus_message_iter_recurse (&iter, &child);
888         *hits = NULL;
889
890         do {
891                 ModestSearchHit *hit;
892
893                 hit = modest_dbus_message_iter_get_search_hit (&child);
894
895                 if (hit) {
896                         *hits = g_list_prepend (*hits, hit);    
897                 }
898
899         } while (dbus_message_iter_next (&child));
900
901         dbus_message_unref (reply);
902
903
904         /* TODO: This is from osso source, do we need it? */
905 #if 0
906         /* Tell TaskNavigator to show "launch banner" */
907         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
908                                             APP_LAUNCH_BANNER_METHOD_PATH,
909                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
910                                             APP_LAUNCH_BANNER_METHOD);
911
912         if (msg == NULL) {
913                 g_warn ("dbus_message_new_method_call failed");
914         }
915
916
917
918         dbus_message_append_args (msg,
919                                   DBUS_TYPE_STRING,
920                                   &service,
921                                   DBUS_TYPE_INVALID);
922
923         b = dbus_connection_send (conn, msg, NULL);
924
925         if (b == NULL) {
926                 ULOG_WARN_F("dbus_connection_send failed");
927         }
928
929         dbus_message_unref (msg);
930 #endif
931
932         return TRUE;
933 }
934
935
936 static ModestAccountHits *
937 modest_dbus_message_iter_get_account_hits (DBusMessageIter *parent)
938 {
939
940         ModestAccountHits *account_hits;
941         int              arg_type;
942         gboolean         error;
943         dbus_bool_t      res;
944         DBusMessageIter  child, traverse;
945
946         error = FALSE;
947         account_hits = g_slice_new0 (ModestAccountHits);
948
949         arg_type = dbus_message_iter_get_arg_type (parent);
950
951         if (arg_type != 'r') {
952                 return NULL;
953         }
954
955         dbus_message_iter_recurse (parent, &child);
956         
957         /* accountid  */
958         arg_type = dbus_message_iter_get_arg_type (&child);
959
960         if (arg_type != DBUS_TYPE_STRING) {
961                 error = TRUE;
962                 goto out;
963         }
964
965         account_hits->account_id = _dbus_iter_get_string_or_null (&child);
966
967         res = dbus_message_iter_next (&child);
968         if (res == FALSE) {
969                 error = TRUE;
970                 goto out;
971         }
972
973         /* account name */
974         arg_type = dbus_message_iter_get_arg_type (&child);
975
976         if (arg_type != DBUS_TYPE_STRING) {
977                 error = TRUE;
978                 goto out;
979         }
980
981         account_hits->account_name = _dbus_iter_get_string_or_null (&child);
982
983         res = dbus_message_iter_next (&child);
984         if (res == FALSE) {
985                 error = TRUE;
986                 goto out;
987         }
988
989         /* list of hits  */
990         dbus_message_iter_recurse (&child, &traverse);
991         account_hits->hits = NULL;
992
993         do {
994                 ModestGetUnreadMessagesHit *hit;
995
996                 hit = modest_dbus_message_iter_get_unread_messages_hit (&traverse);
997                 if (hit) {
998                         account_hits->hits = g_list_prepend (account_hits->hits, hit);
999                 }
1000         } while (dbus_message_iter_next (&traverse));
1001 out:
1002         if (error) {
1003                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
1004                 modest_account_hits_free (account_hits);
1005                 account_hits = NULL;
1006         }
1007
1008         return account_hits;
1009 }
1010
1011 gboolean
1012 libmodest_dbus_client_get_unread_messages (osso_context_t          *osso_ctx,
1013                                            gint msgs_per_account,
1014                                            GList **account_hits_lists)
1015 {
1016
1017         dbus_bool_t res;
1018         DBusMessageIter iter;
1019         DBusMessageIter child;
1020         DBusMessage *reply = NULL;
1021         int arg_type;
1022
1023         DBusConnection *con;
1024         DBusMessage *msg;
1025         dbus_int32_t msgs_per_account_v;
1026         DBusError err;
1027         gint timeout;
1028
1029         if (msgs_per_account < 1) {
1030                 return FALSE;
1031         }
1032
1033         con = osso_get_dbus_connection (osso_ctx);
1034
1035         if (con == NULL) {
1036                 g_warning ("Could not get dbus connection\n");
1037                 return FALSE;
1038
1039         }
1040
1041
1042         msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
1043                 MODEST_DBUS_OBJECT,
1044                 MODEST_DBUS_IFACE,
1045                 MODEST_DBUS_METHOD_GET_UNREAD_MESSAGES);
1046
1047         if (msg == NULL) {
1048                 //ULOG_ERR_F("dbus_message_new_method_call failed");
1049                 return OSSO_ERROR;
1050         }
1051
1052         msgs_per_account_v = (dbus_int32_t) msgs_per_account;
1053
1054         res  = dbus_message_append_args (msg,
1055                                          DBUS_TYPE_INT32, &msgs_per_account,
1056                                          DBUS_TYPE_INVALID);
1057
1058         dbus_message_set_auto_start (msg, TRUE);
1059
1060         timeout = 120000; //milliseconds.
1061
1062         dbus_error_init (&err);
1063         reply = dbus_connection_send_with_reply_and_block (con,
1064                                                            msg, 
1065                                                            timeout,
1066                                                            &err);
1067
1068         dbus_message_unref (msg);
1069
1070         if (!reply) {
1071                 g_warning("%s: dbus_connection_send_with_reply_and_block() error: %s", 
1072                         __FUNCTION__, err.message);
1073                 return FALSE;
1074         }
1075
1076         switch (dbus_message_get_type (reply)) {
1077
1078                 case DBUS_MESSAGE_TYPE_ERROR:
1079                         dbus_set_error_from_message (&err, reply);
1080                         //XXX to GError?!
1081                         dbus_error_free (&err);
1082                         dbus_message_unref (reply);
1083                         return FALSE;
1084
1085                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
1086                         /* ok we are good to go
1087                          * lets drop outa here and handle that */
1088                         break;
1089                 default:
1090                         //ULOG_WARN_F("got unknown message type as reply");
1091                         //retval->type = DBUS_TYPE_STRING;
1092                         //retval->value.s = g_strdup("Invalid return value");
1093                         //XXX to GError?! 
1094                         dbus_message_unref (reply);
1095                         return FALSE;
1096         }
1097
1098         g_debug ("%s: message return", __FUNCTION__);
1099
1100         dbus_message_iter_init (reply, &iter);
1101         arg_type = dbus_message_iter_get_arg_type (&iter);
1102         
1103         dbus_message_iter_recurse (&iter, &child);
1104         *account_hits_lists = NULL;
1105
1106         do {
1107                 ModestAccountHits *account_hits;
1108
1109                 account_hits = modest_dbus_message_iter_get_account_hits (&child);
1110
1111                 if (account_hits) {
1112                         *account_hits_lists = g_list_prepend (*account_hits_lists, account_hits);       
1113                 }
1114
1115         } while (dbus_message_iter_next (&child));
1116
1117         dbus_message_unref (reply);
1118
1119
1120         return TRUE;
1121 }
1122
1123
1124 static void
1125 modest_folder_result_free (ModestFolderResult *item)
1126 {
1127         g_free (item->folder_name);
1128         g_free (item->folder_uri);
1129         g_slice_free (ModestFolderResult, item);
1130 }
1131
1132 void
1133 modest_folder_result_list_free (GList *list)
1134 {
1135         GList *iter;
1136
1137         if (list == NULL) {
1138                 return;
1139         }
1140
1141         for (iter = list; iter; iter = iter->next) {
1142                 modest_folder_result_free ((ModestFolderResult *) iter->data);
1143         }
1144
1145         g_list_free (list);
1146 }
1147
1148
1149 /** Get the values from the complex type (GET_FOLDERS_RESULT_DBUS_TYPE)
1150  * in the D-Bus return message. */
1151 static ModestFolderResult *
1152 modest_dbus_message_iter_get_folder_item (DBusMessageIter *parent)
1153 {
1154         gboolean error = FALSE;
1155         ModestFolderResult *item = g_slice_new0 (ModestFolderResult);
1156
1157         int arg_type = dbus_message_iter_get_arg_type (parent);
1158
1159         if (arg_type != 'r') {
1160                 return NULL;
1161         }
1162
1163         DBusMessageIter  child;
1164         dbus_message_iter_recurse (parent, &child);
1165         
1166         /* folder name: */
1167         arg_type = dbus_message_iter_get_arg_type (&child);
1168
1169         if (arg_type != DBUS_TYPE_STRING) {
1170                 error = TRUE;
1171                 goto out;
1172         }
1173
1174         item->folder_name = _dbus_iter_get_string_or_null (&child);
1175         
1176         
1177         dbus_bool_t res = dbus_message_iter_next (&child);
1178         if (res == FALSE) {
1179                 error = TRUE;
1180                 goto out;
1181         }
1182
1183         /* folder URI:  */
1184         arg_type = dbus_message_iter_get_arg_type (&child);
1185
1186         if (arg_type != DBUS_TYPE_STRING) {
1187                 error = TRUE;
1188                 goto out;
1189         }
1190
1191         item->folder_uri = _dbus_iter_get_string_or_null (&child);
1192
1193
1194 out:
1195         if (error) {
1196                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
1197                 modest_folder_result_free (item);
1198                 item = NULL;
1199         }
1200
1201         return item;
1202 }
1203
1204 /**
1205  * libmodest_dbus_client_get_folders:
1206  * @osso_ctx: A valid #osso_context_t object.
1207  * @folders: A pointer to a valid GList pointer that will contain the folder items
1208  * (ModestFolderResult). The list and the items must be freed by the caller 
1209  * with modest_folder_result_list_free().
1210  *
1211  * This method will obtain a list of folders in the default account.
1212  *
1213  * Upon success TRUE is returned and @folders will include the folders or the list
1214  * might be empty if there are no folders. The returned
1215  * list must be freed with modest_folder_result_list_free ().
1216  *
1217  * NOTE: A folder will only be retrieved if it was previously downloaded by
1218  * modest. This function does also not attempt do to remote refreshes (i.e. IMAP).
1219  * 
1220  * Return value: TRUE if the request succeded or FALSE for an error.
1221  **/
1222 gboolean
1223 libmodest_dbus_client_get_folders (osso_context_t          *osso_ctx,
1224                               GList                  **folders)
1225 {
1226         /* Initialize output argument: */
1227         if (folders)
1228                 *folders = NULL;
1229         else
1230                 return FALSE;
1231
1232         DBusConnection *con = osso_get_dbus_connection (osso_ctx);
1233
1234         if (con == NULL) {
1235                 g_warning ("Could not get dbus connection\n");
1236                 return FALSE;
1237
1238         }
1239
1240         DBusMessage *msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
1241                 MODEST_DBUS_OBJECT,
1242                 MODEST_DBUS_IFACE,
1243                 MODEST_DBUS_METHOD_GET_FOLDERS);
1244
1245     if (msg == NULL) {
1246         //ULOG_ERR_F("dbus_message_new_method_call failed");
1247                 return OSSO_ERROR;
1248     }
1249
1250         dbus_message_set_auto_start (msg, TRUE);
1251
1252         /* Use a long timeout (2 minutes) because the search currently 
1253          * gets folders from the servers. */
1254         gint timeout = 120000;
1255         //osso_rpc_get_timeout (osso_ctx, &timeout);
1256
1257         DBusError err;
1258         dbus_error_init (&err);
1259         DBusMessage *reply = dbus_connection_send_with_reply_and_block (con,
1260                                                            msg, 
1261                                                            timeout,
1262                                                            &err);
1263
1264         dbus_message_unref (msg);
1265         msg = NULL;
1266
1267         if (reply == NULL) {
1268                 g_warning("%s: dbus_connection_send_with_reply_and_block() error:\n   %s", 
1269                         __FUNCTION__, err.message);
1270                 return FALSE;
1271         }
1272
1273         switch (dbus_message_get_type (reply)) {
1274
1275                 case DBUS_MESSAGE_TYPE_ERROR:
1276                         dbus_set_error_from_message (&err, reply);
1277                         //XXX to GError?!
1278                         dbus_error_free (&err);
1279                         dbus_message_unref (reply);
1280                         return FALSE;
1281
1282                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
1283                         /* ok we are good to go
1284                          * lets drop outa here and handle that */
1285                         break;
1286                 default:
1287                         //ULOG_WARN_F("got unknown message type as reply");
1288                         //retval->type = DBUS_TYPE_STRING;
1289                         //retval->value.s = g_strdup("Invalid return value");
1290                         //XXX to GError?! 
1291                         dbus_message_unref (reply);
1292                         return FALSE;
1293         }
1294
1295         g_debug ("%s: message return", __FUNCTION__);
1296
1297         DBusMessageIter iter;
1298         dbus_message_iter_init (reply, &iter);
1299         /* int arg_type = dbus_message_iter_get_arg_type (&iter); */
1300         
1301         DBusMessageIter child;
1302         dbus_message_iter_recurse (&iter, &child);
1303
1304         do {
1305                 ModestFolderResult *item = modest_dbus_message_iter_get_folder_item (&child);
1306
1307                 if (item) {
1308                         *folders = g_list_append (*folders, item);      
1309                 }
1310
1311         } while (dbus_message_iter_next (&child));
1312
1313         dbus_message_unref (reply);
1314
1315
1316         /* TODO: This is from osso source, do we need it? */
1317 #if 0
1318         /* Tell TaskNavigator to show "launch banner" */
1319         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
1320                                             APP_LAUNCH_BANNER_METHOD_PATH,
1321                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
1322                                             APP_LAUNCH_BANNER_METHOD);
1323
1324         if (msg == NULL) {
1325                 g_warn ("dbus_message_new_method_call failed");
1326         }
1327
1328
1329
1330         dbus_message_append_args (msg,
1331                                   DBUS_TYPE_STRING,
1332                                   &service,
1333                                   DBUS_TYPE_INVALID);
1334
1335         b = dbus_connection_send (conn, msg, NULL);
1336
1337         if (b == NULL) {
1338                 ULOG_WARN_F("dbus_connection_send failed");
1339         }
1340
1341         dbus_message_unref (msg);
1342 #endif
1343
1344         return TRUE;
1345 }
1346
1347