f7faf9ef194461bb95e79223f04f539d62366e7c
[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         g_free (account_hits->store_protocol);
404         modest_account_hits_hits_list_free (account_hits->hits);
405         g_slice_free (ModestAccountHits, account_hits);
406 }
407
408 void
409 modest_account_hits_list_free (GList *account_hits_list)
410 {
411         GList *iter;
412
413         if (account_hits_list == NULL) {
414                 return;
415         }
416
417         for (iter = account_hits_list; iter; iter = iter->next) {
418                 modest_account_hits_free ((ModestAccountHits *) iter->data);
419         }
420
421         g_list_free (account_hits_list);
422 }
423
424 static char *
425 _dbus_iter_get_string_or_null (DBusMessageIter *iter)
426 {
427         const char *string = NULL;
428         char       *ret = NULL;
429
430         dbus_message_iter_get_basic (iter, &string);
431         
432         if (string && strlen (string)) {
433                 ret = g_strdup (string);
434         }
435
436         return ret;
437 }
438
439 static guint64
440 _dbus_iter_get_uint64 (DBusMessageIter *iter)
441 {
442         dbus_uint64_t ui64v;
443         guint64       ret;
444
445         ui64v = 0;
446         dbus_message_iter_get_basic (iter, &ui64v);
447
448         ret = (guint64) ui64v;
449
450         return ret;
451 }
452
453
454 static gint64
455 _dbus_iter_get_int64 (DBusMessageIter *iter)
456 {
457         dbus_int64_t i64v;
458         gint64       ret;
459
460         i64v = 0;
461         dbus_message_iter_get_basic (iter, &i64v);
462
463         ret = (gint64) i64v;
464
465         return ret;
466 }
467
468 static gboolean
469 _dbus_iter_get_boolean (DBusMessageIter *iter)
470
471 {
472         dbus_bool_t  val;
473         gboolean     ret;
474
475         val = FALSE;
476         dbus_message_iter_get_basic (iter, &val);
477
478         ret = (gboolean) val;
479
480         return ret;
481 }
482
483 /** Get the values from the complex type (SEARCH_HIT_DBUS_TYPE)
484  * in the D-Bus return message. */
485 static ModestSearchHit *
486 modest_dbus_message_iter_get_search_hit (DBusMessageIter *parent)
487 {
488         ModestSearchHit *hit;
489         DBusMessageIter  child;
490         dbus_bool_t      res;
491         int              arg_type;
492         gboolean         error;
493
494         error = FALSE;
495         hit = g_slice_new0 (ModestSearchHit);
496
497         arg_type = dbus_message_iter_get_arg_type (parent);
498
499         if (arg_type != 'r') {
500                 return NULL;
501         }
502
503         dbus_message_iter_recurse (parent, &child);
504         
505         /* msgid  */
506         arg_type = dbus_message_iter_get_arg_type (&child);
507
508         if (arg_type != DBUS_TYPE_STRING) {
509                 error = TRUE;
510                 goto out;
511         }
512
513         hit->msgid = _dbus_iter_get_string_or_null (&child);
514
515         res = dbus_message_iter_next (&child);
516         if (res == FALSE) {
517                 error = TRUE;
518                 goto out;
519         }
520
521         /* subject  */
522         arg_type = dbus_message_iter_get_arg_type (&child);
523
524         if (arg_type != DBUS_TYPE_STRING) {
525                 error = TRUE;
526                 goto out;
527         }
528
529         hit->subject = _dbus_iter_get_string_or_null (&child);
530
531         res = dbus_message_iter_next (&child);
532         if (res == FALSE) {
533                 error = TRUE;
534                 goto out;
535         }
536
537         /* folder  */
538         arg_type = dbus_message_iter_get_arg_type (&child);
539
540         if (arg_type != DBUS_TYPE_STRING) {
541                 error = TRUE;
542                 goto out;
543         }
544
545         hit->folder = _dbus_iter_get_string_or_null (&child);
546
547         res = dbus_message_iter_next (&child);
548         if (res == FALSE) {
549                 error = TRUE;
550                 goto out;
551         }
552
553         /* sender  */
554         arg_type = dbus_message_iter_get_arg_type (&child);
555
556         if (arg_type != DBUS_TYPE_STRING) {
557                 error = TRUE;
558                 goto out;
559         }
560
561         hit->sender = _dbus_iter_get_string_or_null (&child);
562
563         res = dbus_message_iter_next (&child);
564         if (res == FALSE) {
565                 error = TRUE;
566                 goto out;
567         }
568
569         /* msize  */
570         arg_type = dbus_message_iter_get_arg_type (&child);
571
572         if (arg_type != DBUS_TYPE_UINT64) {
573                 error = TRUE;
574                 goto out;
575         }
576
577         hit->msize = _dbus_iter_get_uint64 (&child);
578
579         res = dbus_message_iter_next (&child);
580         if (res == FALSE) {
581                 error = TRUE;
582                 goto out;
583         }
584
585         /* has_attachment  */
586         arg_type = dbus_message_iter_get_arg_type (&child);
587
588         if (arg_type != DBUS_TYPE_BOOLEAN) {
589                 error = TRUE;
590                 goto out;
591         }
592
593         hit->has_attachment = _dbus_iter_get_boolean (&child); 
594
595         res = dbus_message_iter_next (&child);
596         if (res == FALSE) {
597                 error = TRUE;
598                 goto out;
599         }
600
601         /* is_unread  */
602         arg_type = dbus_message_iter_get_arg_type (&child);
603
604         if (arg_type != DBUS_TYPE_BOOLEAN) {
605                 error = TRUE;
606                 goto out;
607         }
608
609         hit->is_unread = _dbus_iter_get_boolean (&child);  
610
611         res = dbus_message_iter_next (&child);
612         if (res == FALSE) {
613                 error = TRUE;
614                 goto out;
615         }
616
617         /* timestamp  */
618         arg_type = dbus_message_iter_get_arg_type (&child);
619
620         if (arg_type != DBUS_TYPE_INT64) {
621                 error = TRUE;
622                 goto out;
623         }
624
625         hit->timestamp = _dbus_iter_get_int64 (&child); 
626
627         res = dbus_message_iter_next (&child);
628         if (res == TRUE) {
629                 error = TRUE;
630                 goto out;
631         }       
632
633 out:
634         if (error) {
635                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
636                 modest_search_hit_free (hit);
637                 hit = NULL;
638         }
639
640         return hit;
641 }
642
643 static ModestGetUnreadMessagesHit *
644 modest_dbus_message_iter_get_unread_messages_hit (DBusMessageIter *parent)
645 {
646         ModestGetUnreadMessagesHit *hit;
647         DBusMessageIter  child;
648         dbus_bool_t      res;
649         int              arg_type;
650         gboolean         error;
651
652         error = FALSE;
653
654         arg_type = dbus_message_iter_get_arg_type (parent);
655
656         if (arg_type != 'r') {
657                 return NULL;
658         }
659
660         hit = g_slice_new0 (ModestGetUnreadMessagesHit);
661         dbus_message_iter_recurse (parent, &child);
662         
663         /* timestamp  */
664         arg_type = dbus_message_iter_get_arg_type (&child);
665
666         if (arg_type != DBUS_TYPE_INT64) {
667                 error = TRUE;
668                 goto out;
669         }
670
671         hit->timestamp = _dbus_iter_get_int64 (&child); 
672
673         res = dbus_message_iter_next (&child);
674         if (res == FALSE) {
675                 error = TRUE;
676                 goto out;
677         }       
678
679         /* subject  */
680         arg_type = dbus_message_iter_get_arg_type (&child);
681
682         if (arg_type != DBUS_TYPE_STRING) {
683                 error = TRUE;
684                 goto out;
685         }
686
687         hit->subject = _dbus_iter_get_string_or_null (&child);
688
689         res = dbus_message_iter_next (&child);
690         if (res == TRUE) {
691                 error = TRUE;
692                 goto out;
693         }
694
695 out:
696         if (error) {
697                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
698                 g_slice_free (ModestGetUnreadMessagesHit, hit);
699                 hit = NULL;
700         }
701
702         return hit;
703 }
704
705 /**
706  * libmodest_dbus_client_search:
707  * @osso_ctx: A valid #osso_context_t object.
708  * @query: The term to search for.
709  * @folder: An url to specific folder or %NULL to search everywhere.
710  * @start_date: Search hits before this date will be ignored.
711  * @end_date: Search hits after this date will be ignored.
712  * @min_size: Messagers smaller then this size will be ingored.
713  * @flags: A list of flags where to search so the documentation 
714  * of %ModestDBusSearchFlags for details.
715  * @hits: A pointer to a valid GList pointer that will contain the search
716  * hits (ModestSearchHit). The list and the items must be freed by the caller 
717  * with modest_search_hit_list_free().
718  *
719  * This method will search the folder specified by a valid url in @folder or all
720  * known accounts (local and remote) if %NULL for matches of the search term(s)
721  * specified in @query. It is legal to specify 0 in @start_date, @end_date and
722  * @min_size to ignore these parameters during the search otherwise those message
723  * that do not meet the specifed dates or size will be ignored.
724  * Where to search, be it subject, sender or the whole body can be specified by
725  * the @flags parameter.
726  *
727  * Upon success TRUE is returned and @hits will include the search hits or the list
728  * migh be empty if none of the messages matched the search criteria. The returned
729  * list must be freed with modest_search_hit_list_free (). It is save to pass
730  * %NULL to this function so you can call this function on the result list no matter
731  * if a hit was found or not (means the list is empty - i.e. %NULL)
732  * FALSE will only be return if an error during the remote procedure call (rpc) 
733  * occured or if the specified folder could not be found.
734  *
735  * NOTE: The body of a message can only be searched if it was previously downloaded by
736  * modest. This function does also not attempt do to remote searches (i.e. IMAP search).
737  *
738  * Example to search every account for message containing "no":
739  * <informalexample><programlisting>
740  * ModestDBusSearchFlags  flags;
741  * osso_context_t        *osso_context;
742  * GList                 *hits;
743  * GList                 *iter;
744  * gboolean               res;
745  * 
746  * [...] Initialize osso context [...]
747  *
748  * res = libmodest_dbus_client_search (osso_context,
749  *                                     "no",
750  *                                     NULL,
751  *                                     0,
752  *                                     0,
753  *                                     0,
754  *                                     flags,
755  *                                     &hits);
756  * 
757  * for (iter = hits; iter; iter = iter->next) {
758  *      ModestSearchHit *hit = (ModestSearchHit *) iter->data;
759  *      
760  *      [...] Do something with the hit [...]
761  *
762  *      }
763  *
764  *      modest_search_hit_list_free (hits);
765  * </programlisting></informalexample>
766  * 
767  * Return value: TRUE if the search succeded or FALSE for an error during the search
768  **/
769 gboolean
770 libmodest_dbus_client_search (osso_context_t          *osso_ctx,
771                               const gchar             *query,
772                               const gchar             *folder,
773                               time_t                   start_date,
774                               time_t                   end_date,
775                               guint32                  min_size,
776                               ModestDBusSearchFlags    flags,
777                               GList                  **hits)
778 {
779
780         DBusMessage *msg;
781         dbus_bool_t res;
782         DBusConnection *con;
783         DBusMessageIter iter;
784         DBusMessageIter child;
785         DBusMessage *reply = NULL;
786         gint timeout;
787         int arg_type;
788         dbus_int64_t sd_v;
789         dbus_int64_t ed_v;
790         dbus_int32_t flags_v;
791         dbus_uint32_t size_v;
792
793         if (query == NULL) {
794                 return FALSE;
795         }
796
797         con = osso_get_dbus_connection (osso_ctx);
798
799         if (con == NULL) {
800                 g_warning ("Could not get dbus connection\n");
801                 return FALSE;
802
803         }
804
805
806         msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
807                 MODEST_DBUS_OBJECT,
808                 MODEST_DBUS_IFACE,
809                 MODEST_DBUS_METHOD_SEARCH);
810
811     if (msg == NULL) {
812         //ULOG_ERR_F("dbus_message_new_method_call failed");
813                 return OSSO_ERROR;
814     }
815
816         if (folder == NULL) {
817                 folder = "";
818         }
819
820         sd_v = (dbus_int64_t) start_date;
821         ed_v = (dbus_int64_t) end_date;
822         flags_v = (dbus_int32_t) flags;
823         size_v = (dbus_uint32_t) min_size;
824
825         res  = dbus_message_append_args (msg,
826                                          DBUS_TYPE_STRING, &query,
827                                          DBUS_TYPE_STRING, &folder,
828                                          DBUS_TYPE_INT64, &sd_v,
829                                          DBUS_TYPE_INT64, &ed_v,
830                                          DBUS_TYPE_INT32, &flags_v,
831                                          DBUS_TYPE_UINT32, &size_v,
832                                          DBUS_TYPE_INVALID);
833
834         dbus_message_set_auto_start (msg, TRUE);
835
836         /* Use a long timeout (2 minutes) because the search currently 
837          * gets folders and messages from the servers. */
838         timeout = 120000; //milliseconds.
839         //osso_rpc_get_timeout (osso_ctx, &timeout);
840
841     /*printf("DEBUG: %s: Before dbus_connection_send_with_reply_and_block().\n", 
842                 __FUNCTION__); */
843         /* TODO: Detect the timeout somehow. */
844         DBusError err;
845         dbus_error_init (&err);
846         reply = dbus_connection_send_with_reply_and_block (con,
847                                                            msg, 
848                                                            timeout,
849                                                            &err);
850         /* printf("DEBUG: %s: dbus_connection_send_with_reply_and_block() finished.\n", 
851                 __FUNCTION__); */
852
853         dbus_message_unref (msg);
854
855         if (!reply) {
856                 g_warning("%s: dbus_connection_send_with_reply_and_block() error: %s", 
857                         __FUNCTION__, err.message);
858                 return FALSE;
859         }
860
861         switch (dbus_message_get_type (reply)) {
862
863                 case DBUS_MESSAGE_TYPE_ERROR:
864                         dbus_set_error_from_message (&err, reply);
865                         //XXX to GError?!
866                         dbus_error_free (&err);
867                         dbus_message_unref (reply);
868                         return FALSE;
869
870                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
871                         /* ok we are good to go
872                          * lets drop outa here and handle that */
873                         break;
874                 default:
875                         //ULOG_WARN_F("got unknown message type as reply");
876                         //retval->type = DBUS_TYPE_STRING;
877                         //retval->value.s = g_strdup("Invalid return value");
878                         //XXX to GError?! 
879                         dbus_message_unref (reply);
880                         return FALSE;
881         }
882
883         g_debug ("%s: message return", __FUNCTION__);
884
885         dbus_message_iter_init (reply, &iter);
886         arg_type = dbus_message_iter_get_arg_type (&iter);
887         
888         dbus_message_iter_recurse (&iter, &child);
889         *hits = NULL;
890
891         do {
892                 ModestSearchHit *hit;
893
894                 hit = modest_dbus_message_iter_get_search_hit (&child);
895
896                 if (hit) {
897                         *hits = g_list_prepend (*hits, hit);    
898                 }
899
900         } while (dbus_message_iter_next (&child));
901
902         dbus_message_unref (reply);
903
904
905         /* TODO: This is from osso source, do we need it? */
906 #if 0
907         /* Tell TaskNavigator to show "launch banner" */
908         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
909                                             APP_LAUNCH_BANNER_METHOD_PATH,
910                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
911                                             APP_LAUNCH_BANNER_METHOD);
912
913         if (msg == NULL) {
914                 g_warn ("dbus_message_new_method_call failed");
915         }
916
917
918
919         dbus_message_append_args (msg,
920                                   DBUS_TYPE_STRING,
921                                   &service,
922                                   DBUS_TYPE_INVALID);
923
924         b = dbus_connection_send (conn, msg, NULL);
925
926         if (b == NULL) {
927                 ULOG_WARN_F("dbus_connection_send failed");
928         }
929
930         dbus_message_unref (msg);
931 #endif
932
933         return TRUE;
934 }
935
936
937 static ModestAccountHits *
938 modest_dbus_message_iter_get_account_hits (DBusMessageIter *parent)
939 {
940
941         ModestAccountHits *account_hits;
942         int              arg_type;
943         gboolean         error;
944         dbus_bool_t      res;
945         DBusMessageIter  child, traverse;
946
947         error = FALSE;
948         account_hits = g_slice_new0 (ModestAccountHits);
949
950         arg_type = dbus_message_iter_get_arg_type (parent);
951
952         if (arg_type != 'r') {
953                 return NULL;
954         }
955
956         dbus_message_iter_recurse (parent, &child);
957         
958         /* accountid  */
959         arg_type = dbus_message_iter_get_arg_type (&child);
960
961         if (arg_type != DBUS_TYPE_STRING) {
962                 error = TRUE;
963                 goto out;
964         }
965
966         account_hits->account_id = _dbus_iter_get_string_or_null (&child);
967
968         res = dbus_message_iter_next (&child);
969         if (res == FALSE) {
970                 error = TRUE;
971                 goto out;
972         }
973
974         /* account name */
975         arg_type = dbus_message_iter_get_arg_type (&child);
976
977         if (arg_type != DBUS_TYPE_STRING) {
978                 error = TRUE;
979                 goto out;
980         }
981
982         account_hits->account_name = _dbus_iter_get_string_or_null (&child);
983
984         res = dbus_message_iter_next (&child);
985         if (res == FALSE) {
986                 error = TRUE;
987                 goto out;
988         }
989
990         /* store protocol */
991         arg_type = dbus_message_iter_get_arg_type (&child);
992
993         if (arg_type != DBUS_TYPE_STRING) {
994                 error = TRUE;
995                 goto out;
996         }
997
998         account_hits->store_protocol = _dbus_iter_get_string_or_null (&child);
999
1000         res = dbus_message_iter_next (&child);
1001         if (res == FALSE) {
1002                 error = TRUE;
1003                 goto out;
1004         }
1005
1006         /* list of hits  */
1007         dbus_message_iter_recurse (&child, &traverse);
1008         account_hits->hits = NULL;
1009
1010         do {
1011                 ModestGetUnreadMessagesHit *hit;
1012
1013                 hit = modest_dbus_message_iter_get_unread_messages_hit (&traverse);
1014                 if (hit) {
1015                         account_hits->hits = g_list_prepend (account_hits->hits, hit);
1016                 }
1017         } while (dbus_message_iter_next (&traverse));
1018 out:
1019         if (error) {
1020                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
1021                 modest_account_hits_free (account_hits);
1022                 account_hits = NULL;
1023         }
1024
1025         return account_hits;
1026 }
1027
1028 gboolean
1029 libmodest_dbus_client_get_unread_messages (osso_context_t          *osso_ctx,
1030                                            gint msgs_per_account,
1031                                            GList **account_hits_lists)
1032 {
1033
1034         dbus_bool_t res;
1035         DBusMessageIter iter;
1036         DBusMessageIter child;
1037         DBusMessage *reply = NULL;
1038         int arg_type;
1039
1040         DBusConnection *con;
1041         DBusMessage *msg;
1042         dbus_int32_t msgs_per_account_v;
1043         DBusError err;
1044         gint timeout;
1045
1046         if (msgs_per_account < 1) {
1047                 return FALSE;
1048         }
1049
1050         con = osso_get_dbus_connection (osso_ctx);
1051
1052         if (con == NULL) {
1053                 g_warning ("Could not get dbus connection\n");
1054                 return FALSE;
1055
1056         }
1057
1058
1059         msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
1060                 MODEST_DBUS_OBJECT,
1061                 MODEST_DBUS_IFACE,
1062                 MODEST_DBUS_METHOD_GET_UNREAD_MESSAGES);
1063
1064         if (msg == NULL) {
1065                 //ULOG_ERR_F("dbus_message_new_method_call failed");
1066                 return OSSO_ERROR;
1067         }
1068
1069         msgs_per_account_v = (dbus_int32_t) msgs_per_account;
1070
1071         res  = dbus_message_append_args (msg,
1072                                          DBUS_TYPE_INT32, &msgs_per_account,
1073                                          DBUS_TYPE_INVALID);
1074
1075         dbus_message_set_auto_start (msg, TRUE);
1076
1077         timeout = 120000; //milliseconds.
1078
1079         dbus_error_init (&err);
1080         reply = dbus_connection_send_with_reply_and_block (con,
1081                                                            msg, 
1082                                                            timeout,
1083                                                            &err);
1084
1085         dbus_message_unref (msg);
1086
1087         if (!reply) {
1088                 g_warning("%s: dbus_connection_send_with_reply_and_block() error: %s", 
1089                         __FUNCTION__, err.message);
1090                 return FALSE;
1091         }
1092
1093         switch (dbus_message_get_type (reply)) {
1094
1095                 case DBUS_MESSAGE_TYPE_ERROR:
1096                         dbus_set_error_from_message (&err, reply);
1097                         //XXX to GError?!
1098                         dbus_error_free (&err);
1099                         dbus_message_unref (reply);
1100                         return FALSE;
1101
1102                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
1103                         /* ok we are good to go
1104                          * lets drop outa here and handle that */
1105                         break;
1106                 default:
1107                         //ULOG_WARN_F("got unknown message type as reply");
1108                         //retval->type = DBUS_TYPE_STRING;
1109                         //retval->value.s = g_strdup("Invalid return value");
1110                         //XXX to GError?! 
1111                         dbus_message_unref (reply);
1112                         return FALSE;
1113         }
1114
1115         g_debug ("%s: message return", __FUNCTION__);
1116
1117         dbus_message_iter_init (reply, &iter);
1118         arg_type = dbus_message_iter_get_arg_type (&iter);
1119         
1120         dbus_message_iter_recurse (&iter, &child);
1121         *account_hits_lists = NULL;
1122
1123         do {
1124                 ModestAccountHits *account_hits;
1125
1126                 account_hits = modest_dbus_message_iter_get_account_hits (&child);
1127
1128                 if (account_hits) {
1129                         *account_hits_lists = g_list_prepend (*account_hits_lists, account_hits);       
1130                 }
1131
1132         } while (dbus_message_iter_next (&child));
1133
1134         dbus_message_unref (reply);
1135
1136
1137         return TRUE;
1138 }
1139
1140
1141 static void
1142 modest_folder_result_free (ModestFolderResult *item)
1143 {
1144         g_free (item->folder_name);
1145         g_free (item->folder_uri);
1146         g_slice_free (ModestFolderResult, item);
1147 }
1148
1149 void
1150 modest_folder_result_list_free (GList *list)
1151 {
1152         GList *iter;
1153
1154         if (list == NULL) {
1155                 return;
1156         }
1157
1158         for (iter = list; iter; iter = iter->next) {
1159                 modest_folder_result_free ((ModestFolderResult *) iter->data);
1160         }
1161
1162         g_list_free (list);
1163 }
1164
1165
1166 /** Get the values from the complex type (GET_FOLDERS_RESULT_DBUS_TYPE)
1167  * in the D-Bus return message. */
1168 static ModestFolderResult *
1169 modest_dbus_message_iter_get_folder_item (DBusMessageIter *parent)
1170 {
1171         gboolean error = FALSE;
1172         ModestFolderResult *item = g_slice_new0 (ModestFolderResult);
1173
1174         int arg_type = dbus_message_iter_get_arg_type (parent);
1175
1176         if (arg_type != 'r') {
1177                 return NULL;
1178         }
1179
1180         DBusMessageIter  child;
1181         dbus_message_iter_recurse (parent, &child);
1182         
1183         /* folder name: */
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_name = _dbus_iter_get_string_or_null (&child);
1192         
1193         
1194         dbus_bool_t res = dbus_message_iter_next (&child);
1195         if (res == FALSE) {
1196                 error = TRUE;
1197                 goto out;
1198         }
1199
1200         /* folder URI:  */
1201         arg_type = dbus_message_iter_get_arg_type (&child);
1202
1203         if (arg_type != DBUS_TYPE_STRING) {
1204                 error = TRUE;
1205                 goto out;
1206         }
1207
1208         item->folder_uri = _dbus_iter_get_string_or_null (&child);
1209
1210
1211 out:
1212         if (error) {
1213                 g_warning ("%s: Error during unmarshalling", __FUNCTION__);
1214                 modest_folder_result_free (item);
1215                 item = NULL;
1216         }
1217
1218         return item;
1219 }
1220
1221 /**
1222  * libmodest_dbus_client_get_folders:
1223  * @osso_ctx: A valid #osso_context_t object.
1224  * @folders: A pointer to a valid GList pointer that will contain the folder items
1225  * (ModestFolderResult). The list and the items must be freed by the caller 
1226  * with modest_folder_result_list_free().
1227  *
1228  * This method will obtain a list of folders in the default account.
1229  *
1230  * Upon success TRUE is returned and @folders will include the folders or the list
1231  * might be empty if there are no folders. The returned
1232  * list must be freed with modest_folder_result_list_free ().
1233  *
1234  * NOTE: A folder will only be retrieved if it was previously downloaded by
1235  * modest. This function does also not attempt do to remote refreshes (i.e. IMAP).
1236  * 
1237  * Return value: TRUE if the request succeded or FALSE for an error.
1238  **/
1239 gboolean
1240 libmodest_dbus_client_get_folders (osso_context_t          *osso_ctx,
1241                               GList                  **folders)
1242 {
1243         /* Initialize output argument: */
1244         if (folders)
1245                 *folders = NULL;
1246         else
1247                 return FALSE;
1248
1249         DBusConnection *con = osso_get_dbus_connection (osso_ctx);
1250
1251         if (con == NULL) {
1252                 g_warning ("Could not get dbus connection\n");
1253                 return FALSE;
1254
1255         }
1256
1257         DBusMessage *msg = dbus_message_new_method_call (MODEST_DBUS_SERVICE,
1258                 MODEST_DBUS_OBJECT,
1259                 MODEST_DBUS_IFACE,
1260                 MODEST_DBUS_METHOD_GET_FOLDERS);
1261
1262     if (msg == NULL) {
1263         //ULOG_ERR_F("dbus_message_new_method_call failed");
1264                 return OSSO_ERROR;
1265     }
1266
1267         dbus_message_set_auto_start (msg, TRUE);
1268
1269         /* Use a long timeout (2 minutes) because the search currently 
1270          * gets folders from the servers. */
1271         gint timeout = 120000;
1272         //osso_rpc_get_timeout (osso_ctx, &timeout);
1273
1274         DBusError err;
1275         dbus_error_init (&err);
1276         DBusMessage *reply = dbus_connection_send_with_reply_and_block (con,
1277                                                            msg, 
1278                                                            timeout,
1279                                                            &err);
1280
1281         dbus_message_unref (msg);
1282         msg = NULL;
1283
1284         if (reply == NULL) {
1285                 g_warning("%s: dbus_connection_send_with_reply_and_block() error:\n   %s", 
1286                         __FUNCTION__, err.message);
1287                 return FALSE;
1288         }
1289
1290         switch (dbus_message_get_type (reply)) {
1291
1292                 case DBUS_MESSAGE_TYPE_ERROR:
1293                         dbus_set_error_from_message (&err, reply);
1294                         //XXX to GError?!
1295                         dbus_error_free (&err);
1296                         dbus_message_unref (reply);
1297                         return FALSE;
1298
1299                 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
1300                         /* ok we are good to go
1301                          * lets drop outa here and handle that */
1302                         break;
1303                 default:
1304                         //ULOG_WARN_F("got unknown message type as reply");
1305                         //retval->type = DBUS_TYPE_STRING;
1306                         //retval->value.s = g_strdup("Invalid return value");
1307                         //XXX to GError?! 
1308                         dbus_message_unref (reply);
1309                         return FALSE;
1310         }
1311
1312         g_debug ("%s: message return", __FUNCTION__);
1313
1314         DBusMessageIter iter;
1315         dbus_message_iter_init (reply, &iter);
1316         /* int arg_type = dbus_message_iter_get_arg_type (&iter); */
1317         
1318         DBusMessageIter child;
1319         dbus_message_iter_recurse (&iter, &child);
1320
1321         do {
1322                 ModestFolderResult *item = modest_dbus_message_iter_get_folder_item (&child);
1323
1324                 if (item) {
1325                         *folders = g_list_append (*folders, item);      
1326                 }
1327
1328         } while (dbus_message_iter_next (&child));
1329
1330         dbus_message_unref (reply);
1331
1332
1333         /* TODO: This is from osso source, do we need it? */
1334 #if 0
1335         /* Tell TaskNavigator to show "launch banner" */
1336         msg = dbus_message_new_method_call (TASK_NAV_SERVICE,
1337                                             APP_LAUNCH_BANNER_METHOD_PATH,
1338                                             APP_LAUNCH_BANNER_METHOD_INTERFACE,
1339                                             APP_LAUNCH_BANNER_METHOD);
1340
1341         if (msg == NULL) {
1342                 g_warn ("dbus_message_new_method_call failed");
1343         }
1344
1345
1346
1347         dbus_message_append_args (msg,
1348                                   DBUS_TYPE_STRING,
1349                                   &service,
1350                                   DBUS_TYPE_INVALID);
1351
1352         b = dbus_connection_send (conn, msg, NULL);
1353
1354         if (b == NULL) {
1355                 ULOG_WARN_F("dbus_connection_send failed");
1356         }
1357
1358         dbus_message_unref (msg);
1359 #endif
1360
1361         return TRUE;
1362 }
1363
1364