* don't try to open file:-uri's, and don't add them to CSMs
[modest] / src / maemo / modest-platform.c
1 /* Copyright (c) 2006, Nokia Corporation
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  * * Redistributions of source code must retain the above copyright
9  *   notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  *   notice, this list of conditions and the following disclaimer in the
12  *   documentation and/or other materials provided with the distribution.
13  * * Neither the name of the Nokia Corporation nor the names of its
14  *   contributors may be used to endorse or promote products derived from
15  *   this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20  * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <config.h>
31 #include <glib/gi18n.h>
32 #include <modest-platform.h>
33 #include <modest-runtime.h>
34 #include <modest-main-window.h>
35 #include <modest-header-view.h>
36 #include "maemo/modest-maemo-global-settings-dialog.h"
37 #include "modest-widget-memory.h"
38 #include <modest-hildon-includes.h>
39 #include <modest-maemo-utils.h>
40 #include <dbus_api/modest-dbus-callbacks.h>
41 #include <maemo/modest-osso-autosave-callbacks.h>
42 #include <libosso.h>
43 #include <tny-maemo-conic-device.h>
44 #include <tny-simple-list.h>
45 #include <tny-folder.h>
46 #include <tny-camel-imap-store-account.h>
47 #include <tny-camel-pop-store-account.h>
48 #include <gtk/gtkicontheme.h>
49 #include <gtk/gtkmenuitem.h>
50 #include <gtk/gtkmain.h>
51 #include <modest-text-utils.h>
52 #include "modest-tny-folder.h"
53 #include "modest-tny-account.h"
54 #include <string.h>
55 #include <libgnomevfs/gnome-vfs-mime-utils.h>
56
57 #ifdef MODEST_HAVE_ABOOK
58 #include <libosso-abook/osso-abook.h>
59 #endif /*MODEST_HAVE_ABOOK*/
60
61 #ifdef MODEST_HAVE_LIBALARM
62 #include <alarmd/alarm_event.h> /* For alarm_event_add(), etc. */
63 #endif /*MODEST_HAVE_LIBALARM*/
64
65
66 #define HILDON_OSSO_URI_ACTION "uri-action"
67 #define URI_ACTION_COPY "copy:"
68
69 static void     
70 on_modest_conf_update_interval_changed (ModestConf* self, 
71                                         const gchar *key, 
72                                         ModestConfEvent event,
73                                         ModestConfNotificationId id, 
74                                         gpointer user_data)
75 {
76         g_return_if_fail (key);
77         
78         if (strcmp (key, MODEST_CONF_UPDATE_INTERVAL) == 0) {
79                 const guint update_interval_minutes = 
80                         modest_conf_get_int (self, MODEST_CONF_UPDATE_INTERVAL, NULL);
81                 modest_platform_set_update_interval (update_interval_minutes);
82         }
83 }
84
85
86
87 static gboolean
88 check_required_files (void)
89 {
90         FILE *mcc_file = modest_maemo_open_mcc_mapping_file ();
91         if (!mcc_file) {
92                 g_printerr ("modest: check for mcc file failed\n");
93                 return FALSE;
94         } else 
95                 fclose (mcc_file);
96         
97         if (access(MODEST_PROVIDER_DATA_FILE, R_OK) != 0 &&
98             access(MODEST_MAEMO_PROVIDER_DATA_FILE, R_OK) != 0) {
99                 g_printerr ("modest: cannot find providers data\n");
100                 return FALSE;
101         }
102         
103         return TRUE;
104 }
105
106
107 /* the gpointer here is the osso_context. */
108 gboolean
109 modest_platform_init (int argc, char *argv[])
110 {
111         osso_context_t *osso_context;
112         
113         osso_hw_state_t hw_state = { 0 };
114         DBusConnection *con;    
115         GSList *acc_names;
116         
117         if (!check_required_files ()) {
118                 g_printerr ("modest: missing required files\n");
119                 return FALSE;
120         }
121         
122         osso_context =  osso_initialize(PACKAGE,PACKAGE_VERSION,
123                                         FALSE, NULL);   
124         if (!osso_context) {
125                 g_printerr ("modest: failed to acquire osso context\n");
126                 return FALSE;
127         }
128         modest_maemo_utils_set_osso_context (osso_context);
129
130         if ((con = osso_get_dbus_connection (osso_context)) == NULL) {
131                 g_printerr ("modest: could not get dbus connection\n");
132                 return FALSE;
133         }
134
135         /* Add a D-Bus handler to be used when the main osso-rpc 
136          * D-Bus handler has not handled something.
137          * We use this for D-Bus methods that need to use more complex types 
138          * than osso-rpc supports. 
139          */
140         if (!dbus_connection_add_filter (con,
141                                          modest_dbus_req_filter,
142                                          NULL,
143                                          NULL)) {
144
145                 g_printerr ("modest: Could not add D-Bus filter\n");
146                 return FALSE;
147         }
148
149         /* Register our simple D-Bus callbacks, via the osso API: */
150         osso_return_t result = osso_rpc_set_cb_f(osso_context, 
151                                MODEST_DBUS_SERVICE, 
152                                MODEST_DBUS_OBJECT, 
153                                MODEST_DBUS_IFACE,
154                                modest_dbus_req_handler, NULL /* user_data */);
155         if (result != OSSO_OK) {
156                 g_printerr ("modest: Error setting D-BUS callback (%d)\n", result);
157                 return FALSE;
158         }
159
160         /* Register hardware event dbus callback: */
161         hw_state.shutdown_ind = TRUE;
162         osso_hw_set_event_cb(osso_context, NULL, NULL, NULL);
163
164         /* Register osso auto-save callbacks: */
165         result = osso_application_set_autosave_cb (osso_context, 
166                 modest_on_osso_application_autosave, NULL /* user_data */);
167         if (result != OSSO_OK) {
168                 g_printerr ("modest: osso_application_set_autosave_cb() failed.\n");
169                 return FALSE;
170         }
171         
172
173         /* Make sure that the update interval is changed whenever its gconf key 
174          * is changed */
175         /* CAUTION: we're not using here the
176            modest_conf_listen_to_namespace because we know that there
177            are other parts of Modest listening for this namespace, so
178            we'll receive the notifications anyway. We basically do not
179            use it because there is no easy way to do the
180            modest_conf_forget_namespace */
181         ModestConf *conf = modest_runtime_get_conf ();
182         g_signal_connect (G_OBJECT(conf),
183                           "key_changed",
184                           G_CALLBACK (on_modest_conf_update_interval_changed), 
185                           NULL);
186
187         /* only force the setting of the default interval, if there are actually
188          * any accounts */
189         acc_names = modest_account_mgr_account_names (modest_runtime_get_account_mgr(), TRUE);
190         if (acc_names) {
191                 /* Get the initial update interval from gconf: */
192                 on_modest_conf_update_interval_changed(conf, MODEST_CONF_UPDATE_INTERVAL,
193                                                        MODEST_CONF_EVENT_KEY_CHANGED, 0, NULL);
194                 modest_account_mgr_free_account_names (acc_names);
195         }
196
197         
198 #ifdef MODEST_HAVE_ABOOK
199         /* initialize the addressbook */
200         if (!osso_abook_init (&argc, &argv, osso_context)) {
201                 g_printerr ("modest: failed to initialized addressbook\n");
202                 return FALSE;
203         }
204 #endif /*MODEST_HAVE_ABOOK*/
205
206         return TRUE;
207 }
208
209 gboolean
210 modest_platform_uninit (void)
211 {
212         osso_context_t *osso_context =
213                 modest_maemo_utils_get_osso_context ();
214         if (osso_context)
215                 osso_deinitialize (osso_context);
216
217         return TRUE;
218 }
219
220
221
222
223 TnyDevice*
224 modest_platform_get_new_device (void)
225 {
226         return TNY_DEVICE (tny_maemo_conic_device_new ());
227 }
228
229 gchar*
230 modest_platform_get_file_icon_name (const gchar* name, const gchar* mime_type,
231                                     gchar **effective_mime_type)
232 {
233         GString *mime_str = NULL;
234         gchar *icon_name  = NULL;
235         gchar **icons, **cursor;
236         
237         if (!mime_type || g_ascii_strcasecmp (mime_type, "application/octet-stream") == 0) 
238                 mime_str = g_string_new (gnome_vfs_get_mime_type_for_name (name));
239         else {
240                 mime_str = g_string_new (mime_type);
241                 g_string_ascii_down (mime_str);
242         }
243         
244         icons = hildon_mime_get_icon_names (mime_str->str, NULL);
245         
246         for (cursor = icons; cursor; ++cursor) {
247                 if (!g_ascii_strcasecmp (*cursor, "gnome-mime-message") ||
248                     !g_ascii_strcasecmp (*cursor, "gnome-mime-message-rfc822")) {
249                         icon_name = g_strdup ("qgn_list_messagin");
250                         break;
251                 } else if (gtk_icon_theme_has_icon (gtk_icon_theme_get_default(), *cursor)) {
252                         icon_name = g_strdup (*cursor);
253                         break;
254                 }
255         }
256         g_strfreev (icons);
257
258         if (effective_mime_type)
259                 *effective_mime_type = g_string_free (mime_str, FALSE);
260         else
261                 g_string_free (mime_str, TRUE);
262         
263         return icon_name;
264 }
265
266
267 static gboolean
268 checked_hildon_uri_open (const gchar *uri, HildonURIAction *action)
269 {
270         GError *err = NULL;
271         gboolean result;
272
273         g_return_val_if_fail (uri, FALSE);
274         
275         result = hildon_uri_open (uri, action, &err);
276         if (!result) {
277                 g_printerr ("modest: hildon_uri_open ('%s', %p) failed: %s",
278                             uri, action,  err && err->message ? err->message : "unknown error");
279                 if (err)
280                         g_error_free (err);
281         }
282         return result;
283 }
284
285
286
287 gboolean 
288 modest_platform_activate_uri (const gchar *uri)
289 {
290         HildonURIAction *action;
291         gboolean result = FALSE;
292         GSList *actions, *iter = NULL;
293         
294         g_return_val_if_fail (uri, FALSE);
295         if (!uri)
296                 return FALSE;
297
298         /* don't try to activate file: uri's -- they might confuse the user,
299          * and/or might have security implications */
300         if (!g_str_has_prefix (uri, "file:")) {
301                 
302                 actions = hildon_uri_get_actions_by_uri (uri, -1, NULL);
303                 
304                 for (iter = actions; iter; iter = g_slist_next (iter)) {
305                         action = (HildonURIAction*) iter->data;
306                         if (action && strcmp (hildon_uri_action_get_service (action),
307                                               "com.nokia.modest") == 0) {
308                                 result = checked_hildon_uri_open (uri, action);
309                                 break;
310                         }
311                 }
312                 
313                 /* if we could not open it with email, try something else */
314                 if (!result)
315                         result = checked_hildon_uri_open (uri, NULL);   
316         } 
317         
318         if (!result) {
319                 ModestWindow *parent =
320                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
321                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
322                                                 _("mcen_ib_unsupported_link"));
323                 g_warning ("%s: cannot open uri '%s'", __FUNCTION__,uri);
324         } 
325         
326         return result;
327 }
328
329 gboolean 
330 modest_platform_activate_file (const gchar *path, const gchar *mime_type)
331 {
332         gint result = 0;
333         DBusConnection *con;
334         gchar *uri_path = NULL;
335         
336         uri_path = g_strconcat ("file://", path, NULL); 
337         con = osso_get_dbus_connection (modest_maemo_utils_get_osso_context());
338         
339         if (mime_type)
340                 result = hildon_mime_open_file_with_mime_type (con, uri_path, mime_type);
341         if (result != 1)
342                 result = hildon_mime_open_file (con, uri_path);
343         if (result != 1)
344                 modest_platform_run_information_dialog (NULL, _("mcen_ni_noregistered_viewer"));
345         
346         return result != 1;
347 }
348
349 typedef struct  {
350         GSList *actions;
351         gchar  *uri;
352 } ModestPlatformPopupInfo;
353
354 static gboolean
355 delete_uri_popup (GtkWidget *menu,
356                   GdkEvent *event,
357                   gpointer userdata)
358 {
359         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
360
361         g_free (popup_info->uri);
362         hildon_uri_free_actions (popup_info->actions);
363
364         return FALSE;
365 }
366
367 static void
368 activate_uri_popup_item (GtkMenuItem *menu_item,
369                          gpointer userdata)
370 {
371         GSList *node;
372         ModestPlatformPopupInfo *popup_info = (ModestPlatformPopupInfo *) userdata;
373         const gchar* action_name;
374
375         action_name = g_object_get_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION);
376         if (!action_name) {
377                 g_printerr ("modest: no action name defined\n");
378                 return;
379         }
380
381         /* special handling for the copy menu item -- copy the uri to the clipboard */
382         /* if it's a copy thingy, the uri will look like 'copy:http://slashdot.org' */
383         if (g_str_has_prefix (action_name, URI_ACTION_COPY)) {
384                 GtkClipboard *clipboard = gtk_clipboard_get (GDK_NONE);
385                 action_name += strlen(URI_ACTION_COPY); /* jump past the prefix */
386
387                 if (g_str_has_prefix (action_name, "mailto:")) /* ignore mailto: prefixes */
388                         action_name += strlen ("mailto:");
389                 
390                 gtk_clipboard_set_text (clipboard, action_name, strlen (action_name));
391                 return; /* we're done */
392         }
393         
394         /* now, the real uri-actions... */
395         for (node = popup_info->actions; node != NULL; node = g_slist_next (node)) {
396                 HildonURIAction *action = (HildonURIAction *) node->data;
397                 if (strcmp (action_name, hildon_uri_action_get_name (action))==0) {
398                         if (!checked_hildon_uri_open (popup_info->uri, action)) {
399                                 ModestWindow *parent =
400                                         modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
401                                 hildon_banner_show_information (parent ? GTK_WIDGET(parent): NULL, NULL,
402                                                                 _("mcen_ib_unsupported_link"));
403                         }
404                         break;
405                 }
406         }
407 }
408
409 gboolean 
410 modest_platform_show_uri_popup (const gchar *uri)
411 {
412         GSList *actions_list;
413
414         if (uri == NULL)
415                 return FALSE;
416         
417         actions_list = hildon_uri_get_actions_by_uri (uri, -1, NULL);
418         if (actions_list) {
419
420                 GtkWidget *menu = gtk_menu_new ();
421                 ModestPlatformPopupInfo *popup_info = g_new0 (ModestPlatformPopupInfo, 1);
422
423                 /* don't add actions for file: uri's -- they might confuse the user,
424                  * and/or might have security implications
425                  * we still allow to copy the url though
426                  */
427                 if (!g_str_has_prefix (uri, "file:")) {                 
428                 
429                         GSList *node;                   
430                         popup_info->actions = actions_list;
431                         popup_info->uri = g_strdup (uri);
432                         
433                         for (node = actions_list; node != NULL; node = g_slist_next (node)) {
434                                 GtkWidget *menu_item;
435                                 const gchar *action_name;
436                                 const gchar *translation_domain;
437                                 HildonURIAction *action = (HildonURIAction *) node->data;
438                                 action_name = hildon_uri_action_get_name (action);
439                                 translation_domain = hildon_uri_action_get_translation_domain (action);
440                                 menu_item = gtk_menu_item_new_with_label (dgettext(translation_domain, action_name));
441                                 g_object_set_data (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION, (gpointer)action_name);  /* hack */
442                                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),
443                                                   popup_info);
444                                 
445                                 if (hildon_uri_is_default_action (action, NULL)) {
446                                         gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menu_item);
447                                 } else {
448                                         gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
449                                 }
450                                 gtk_widget_show (menu_item);
451                         }
452                 }
453
454                 /* always add the copy item */
455                 GtkWidget* menu_item = gtk_menu_item_new_with_label (dgettext("osso-uri",
456                                                                               "uri_link_copy_link_location"));
457                 g_object_set_data_full (G_OBJECT(menu_item), HILDON_OSSO_URI_ACTION,
458                                         g_strconcat (URI_ACTION_COPY, uri, NULL),
459                                         g_free);
460                 g_signal_connect (G_OBJECT (menu_item), "activate", G_CALLBACK (activate_uri_popup_item),NULL);
461                 gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
462                 gtk_widget_show (menu_item);
463
464                 
465                 /* and what to do when the link is deleted */
466                 g_signal_connect (G_OBJECT (menu), "delete-event", G_CALLBACK (delete_uri_popup), popup_info);
467                 gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, 1, gtk_get_current_event_time ());
468                                                   
469         } else {
470                 hildon_banner_show_information (NULL, NULL, _("mcen_ib_unsupported_link"));
471         }
472         
473         return TRUE;
474 }
475
476
477 GdkPixbuf*
478 modest_platform_get_icon (const gchar *name)
479 {
480         GError *err = NULL;
481         GdkPixbuf* pixbuf = NULL;
482         GtkIconTheme *current_theme = NULL;
483
484         g_return_val_if_fail (name, NULL);
485
486         /* strlen == 0 is not really an error; it just
487          * means the icon is not available
488          */
489         if (!name || strlen(name) == 0)
490                 return NULL;
491         
492 #if 0 /* do we still need this? */
493         if (g_str_has_suffix (name, ".png")) { /*FIXME: hack*/
494                 pixbuf = gdk_pixbuf_new_from_file (name, &err);
495                 if (!pixbuf) {
496                         g_printerr ("modest: error loading icon '%s': %s\n",
497                                     name, err->message);
498                         g_error_free (err);
499                         return NULL;
500                 }
501                 return pixbuf;
502         }
503 #endif /* */
504         current_theme = gtk_icon_theme_get_default ();
505         pixbuf = gtk_icon_theme_load_icon (current_theme, name, 26,
506                                            GTK_ICON_LOOKUP_NO_SVG,
507                                            &err);
508         if (!pixbuf) {
509                 g_printerr ("modest: error loading theme icon '%s': %s\n",
510                             name, err->message);
511                 g_error_free (err);
512         } 
513         return pixbuf;
514 }
515
516 const gchar*
517 modest_platform_get_app_name (void)
518 {
519         return _("mcen_ap_name");
520 }
521
522 static void 
523 entry_insert_text (GtkEditable *editable,
524                    const gchar *text,
525                    gint         length,
526                    gint        *position,
527                    gpointer     data)
528 {
529         gchar *chars;
530         gint chars_length;
531
532         chars = gtk_editable_get_chars (editable, 0, -1);
533         chars_length = g_utf8_strlen (chars, -1);
534
535         /* Show WID-INF036 */
536         if (chars_length >= 20) {
537                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), NULL,
538                                                  _CS("ckdg_ib_maximum_characters_reached"));
539         } else {
540                 if (modest_text_utils_is_forbidden_char (*text, FOLDER_NAME_FORBIDDEN_CHARS)) {
541                         /* Show an error */
542                         gchar *tmp, *msg;
543                         
544                         tmp = g_strndup (folder_name_forbidden_chars, 
545                                          FOLDER_NAME_FORBIDDEN_CHARS_LENGTH);
546                         msg = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), tmp);
547                         hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (data)), 
548                                                          NULL, msg);
549                         g_free (msg);
550                         g_free (tmp);
551                 } else {        
552                         /* Write the text in the entry if it's valid */
553                         g_signal_handlers_block_by_func (editable,
554                                                          (gpointer) entry_insert_text, data);
555                         gtk_editable_insert_text (editable, text, length, position);
556                         g_signal_handlers_unblock_by_func (editable,
557                                                            (gpointer) entry_insert_text, data);
558                 }
559         }
560         /* Do not allow further processing */
561         g_signal_stop_emission_by_name (editable, "insert_text");
562 }
563
564 static void
565 entry_changed (GtkEditable *editable,
566                gpointer     user_data)
567 {
568         gchar *chars;
569         GtkWidget *ok_button;
570         GList *buttons;
571
572         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (user_data)->action_area));
573         ok_button = GTK_WIDGET (buttons->next->data);
574         
575         chars = gtk_editable_get_chars (editable, 0, -1);
576         g_return_if_fail (chars != NULL);
577
578         
579         if (g_utf8_strlen (chars,-1) >= 21)
580                 hildon_banner_show_information  (gtk_widget_get_parent (GTK_WIDGET (user_data)), NULL,
581                                                  _CS("ckdg_ib_maximum_characters_reached"));
582         else
583                 gtk_widget_set_sensitive (ok_button, modest_text_utils_validate_folder_name(chars));
584                 
585         /* Free */
586         g_list_free (buttons);
587         g_free (chars);
588 }
589
590 static guint
591 checked_hildon_sort_dialog_add_sort_key (HildonSortDialog *dialog, const gchar* key, guint max)
592 {
593         gint sort_key;
594         
595         g_return_val_if_fail (dialog && HILDON_IS_SORT_DIALOG(dialog), 0);
596         g_return_val_if_fail (key, 0);
597         
598         sort_key = hildon_sort_dialog_add_sort_key (dialog, key);
599         if (sort_key < 0 || sort_key >= max) {
600                 g_warning ("%s: out of range (%d) for %s", __FUNCTION__, sort_key, key);
601                 return 0;
602         } else
603                 return (guint)sort_key; 
604 }
605
606
607 static void
608 launch_sort_headers_dialog (GtkWindow *parent_window,
609                             HildonSortDialog *dialog)
610 {
611         ModestHeaderView *header_view = NULL;
612         GList *cols = NULL;
613         GtkSortType sort_type;
614         gint sort_key;
615         gint default_key = 0;
616         gint result;
617         gboolean outgoing = FALSE;
618         gint current_sort_colid = -1;
619         GtkSortType current_sort_type;
620         gint attachments_sort_id;
621         gint priority_sort_id;
622         GtkTreeSortable *sortable;
623         
624         /* Get header window */
625         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
626                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
627                                                                                       MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
628         }
629         if (!header_view)
630                 return;
631         
632         /* Add sorting keys */
633         cols = modest_header_view_get_columns (header_view);
634         if (cols == NULL) 
635                 return;
636 #define SORT_ID_NUM 6
637         int sort_model_ids[SORT_ID_NUM];
638         int sort_ids[SORT_ID_NUM];
639
640         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
641                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
642
643         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_sender_recipient"),
644                                                             SORT_ID_NUM - 1);
645         if (outgoing) {
646                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
647                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
648         } else {
649                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
650                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
651         }
652
653         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_date"),
654                                                             SORT_ID_NUM - 1);
655         if (outgoing) {
656                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
657                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
658         } else {
659                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
660                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
661         }
662         default_key = sort_key;
663
664         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_subject"),
665                                                             SORT_ID_NUM -1);
666         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
667         if (outgoing)
668                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
669         else
670                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
671
672         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_attachment"),
673                                                             SORT_ID_NUM - 1);
674         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
675         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
676         attachments_sort_id = sort_key;
677
678         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_size"),
679                                                             SORT_ID_NUM - 1);
680         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
681         sort_ids[sort_key] = 0;
682
683         sort_key = checked_hildon_sort_dialog_add_sort_key (dialog, _("mcen_li_sort_priority"),
684                                                             SORT_ID_NUM - 1);
685         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
686         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
687         priority_sort_id = sort_key;
688         
689         sortable = GTK_TREE_SORTABLE (gtk_tree_model_filter_get_model
690                                       (GTK_TREE_MODEL_FILTER (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)))));
691         /* Launch dialogs */
692         if (!gtk_tree_sortable_get_sort_column_id (sortable,
693                                                    &current_sort_colid, &current_sort_type)) {
694                 hildon_sort_dialog_set_sort_key (dialog, default_key);
695                 hildon_sort_dialog_set_sort_order (dialog, GTK_SORT_DESCENDING);
696         } else {
697                 hildon_sort_dialog_set_sort_order (dialog, current_sort_type);
698                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
699                         gpointer flags_sort_type_pointer;
700                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
701                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
702                                 hildon_sort_dialog_set_sort_key (dialog, priority_sort_id);
703                         else
704                                 hildon_sort_dialog_set_sort_key (dialog, attachments_sort_id);
705                 } else {
706                         gint current_sort_keyid = 0;
707                         while (current_sort_keyid < 6) {
708                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
709                                         break;
710                                 else 
711                                         current_sort_keyid++;
712                         }
713                         hildon_sort_dialog_set_sort_key (dialog, current_sort_keyid);
714                 }
715         }
716
717         result = gtk_dialog_run (GTK_DIALOG (dialog));
718         if (result == GTK_RESPONSE_OK) {
719                 sort_key = hildon_sort_dialog_get_sort_key (dialog);
720                 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
721                         g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
722                         sort_key = 0;
723                 }
724
725                 sort_type = hildon_sort_dialog_get_sort_order (dialog);
726                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
727                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
728                                            GINT_TO_POINTER (sort_ids[sort_key]));
729                         /* This is a hack to make it resort rows always when flag fields are
730                          * selected. If we do not do this, changing sort field from priority to
731                          * attachments does not work */
732                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
733                 } else {
734                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
735                                                                  sort_model_ids[sort_key]);
736                 }
737
738                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
739                 gtk_tree_sortable_sort_column_changed (sortable);
740         }
741
742         modest_widget_memory_save (modest_runtime_get_conf (),
743                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
744         
745         /* free */
746         g_list_free(cols);      
747 }
748
749
750
751 static void
752 on_response (GtkDialog *dialog,
753              gint response,
754              gpointer user_data)
755 {
756         GList *child_vbox, *child_hbox;
757         GtkWidget *hbox, *entry;
758         TnyFolderStore *parent;
759         const gchar *new_name;
760         gboolean exists;
761
762         if (response != GTK_RESPONSE_ACCEPT)
763                 return;
764         
765         /* Get entry */
766         child_vbox = gtk_container_get_children (GTK_CONTAINER (dialog->vbox));
767         hbox = child_vbox->data;
768         child_hbox = gtk_container_get_children (GTK_CONTAINER (hbox));
769         entry = child_hbox->next->data;
770         
771         parent = TNY_FOLDER_STORE (user_data);
772         new_name = gtk_entry_get_text (GTK_ENTRY (entry));
773         exists = FALSE;
774         
775         /* Look for another folder with the same name */
776         if (modest_tny_folder_has_subfolder_with_name (parent, 
777                                                        new_name,
778                                                        TRUE)) {
779                 exists = TRUE;
780         }
781         
782         if (!exists) {
783                 if (TNY_IS_ACCOUNT (parent) &&
784                     modest_tny_account_is_virtual_local_folders (TNY_ACCOUNT (parent)) &&
785                     modest_tny_local_folders_account_folder_name_in_use (MODEST_TNY_LOCAL_FOLDERS_ACCOUNT (parent),
786                                                                          new_name)) {
787                         exists = TRUE;
788                 }
789         }
790         
791         if (exists) {
792                 
793                 /* Show an error */
794                 hildon_banner_show_information (gtk_widget_get_parent (GTK_WIDGET (dialog)), 
795                                                 NULL, _CS("ckdg_ib_folder_already_exists"));
796                 /* Select the text */
797                 gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
798                 gtk_widget_grab_focus (entry);
799                 /* Do not close the dialog */
800                 g_signal_stop_emission_by_name (dialog, "response");
801         }
802 }
803
804
805
806 static gint
807 modest_platform_run_folder_name_dialog (GtkWindow *parent_window,
808                                         TnyFolderStore *parent,
809                                         const gchar *dialog_title,
810                                         const gchar *label_text,
811                                         const gchar *suggested_name,
812                                         gchar **folder_name)
813 {
814         GtkWidget *accept_btn = NULL; 
815         GtkWidget *dialog, *entry, *label, *hbox;
816         GList *buttons = NULL;
817         gint result;
818
819         /* Ask the user for the folder name */
820         dialog = gtk_dialog_new_with_buttons (dialog_title,
821                                               parent_window,
822                                               GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_DESTROY_WITH_PARENT,
823                                               _("mcen_bd_dialog_ok"),
824                                               GTK_RESPONSE_ACCEPT,
825                                               _("mcen_bd_dialog_cancel"),
826                                               GTK_RESPONSE_REJECT,
827                                               NULL);
828
829         /* Add accept button (with unsensitive handler) */
830         buttons = gtk_container_get_children (GTK_CONTAINER (GTK_DIALOG (dialog)->action_area));
831         accept_btn = GTK_WIDGET (buttons->next->data);
832         /* Create label and entry */
833         label = gtk_label_new (label_text);
834         /* TODO: check that the suggested name does not exist */
835         /* We set 21 as maximum because we want to show WID-INF036
836            when the user inputs more that 20 */
837         entry = gtk_entry_new_with_max_length (21);
838         if (suggested_name)
839                 gtk_entry_set_text (GTK_ENTRY (entry), suggested_name);
840         else
841                 gtk_entry_set_text (GTK_ENTRY (entry), _("mcen_ia_default_folder_name"));
842         gtk_entry_select_region (GTK_ENTRY (entry), 0, -1);
843
844         /* Connect to the response method to avoid closing the dialog
845            when an invalid name is selected*/
846         g_signal_connect (dialog,
847                           "response",
848                           G_CALLBACK (on_response),
849                           parent);
850
851         /* Track entry changes */
852         g_signal_connect (entry,
853                           "insert-text",
854                           G_CALLBACK (entry_insert_text),
855                           dialog);
856         g_signal_connect (entry,
857                           "changed",
858                           G_CALLBACK (entry_changed),
859                           dialog);
860
861         /* Create the hbox */
862         hbox = gtk_hbox_new (FALSE, 12);
863         gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, FALSE, 0);
864         gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, FALSE, 0);
865
866         /* Add hbox to dialog */
867         gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), 
868                             hbox, FALSE, FALSE, 0);
869         
870         gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
871         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
872         
873         result = gtk_dialog_run (GTK_DIALOG(dialog));
874         if (result == GTK_RESPONSE_ACCEPT)
875                 *folder_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry)));
876
877         gtk_widget_destroy (dialog);
878
879         while (gtk_events_pending ())
880                 gtk_main_iteration ();
881
882         return result;
883 }
884
885 gint
886 modest_platform_run_new_folder_dialog (GtkWindow *parent_window,
887                                        TnyFolderStore *parent_folder,
888                                        gchar *suggested_name,
889                                        gchar **folder_name)
890 {
891         gchar *real_suggested_name = NULL;
892         gint result;
893
894         if(suggested_name == NULL)
895         {
896                 const gchar *default_name = _("mcen_ia_default_folder_name");
897                 unsigned int i;
898                 gchar num_str[3];
899
900                 for(i = 0; i < 100; ++ i) {
901                         gboolean exists = FALSE;
902
903                         sprintf(num_str, "%.2u", i);
904
905                         if (i == 0)
906                                 real_suggested_name = g_strdup (default_name);
907                         else
908                                 real_suggested_name = g_strdup_printf (_("mcen_ia_default_folder_name_s"),
909                                                                        num_str);
910                         exists = modest_tny_folder_has_subfolder_with_name (parent_folder,
911                                                                             real_suggested_name,
912                                                                             TRUE);
913
914                         if (!exists)
915                                 break;
916
917                         g_free (real_suggested_name);
918                 }
919
920                 /* Didn't find a free number */
921                 if (i == 100)
922                         real_suggested_name = g_strdup (default_name);
923         } else {
924                 real_suggested_name = suggested_name;
925         }
926
927         result = modest_platform_run_folder_name_dialog (parent_window, 
928                                                          parent_folder,
929                                                          _("mcen_ti_new_folder"),
930                                                          _("mcen_fi_new_folder_name"),
931                                                          real_suggested_name,
932                                                          folder_name);
933         if (suggested_name == NULL)
934                 g_free(real_suggested_name);
935
936         return result;
937 }
938
939 gint
940 modest_platform_run_rename_folder_dialog (GtkWindow *parent_window,
941                                           TnyFolderStore *parent_folder,
942                                           const gchar *suggested_name,
943                                           gchar **folder_name)
944 {
945         g_return_val_if_fail (TNY_IS_FOLDER_STORE (parent_folder), GTK_RESPONSE_REJECT);
946
947         return modest_platform_run_folder_name_dialog (parent_window, 
948                                                        parent_folder,
949                                                        _HL("ckdg_ti_rename_folder"),
950                                                        _HL("ckdg_fi_rename_name"),
951                                                        suggested_name,
952                                                        folder_name);
953 }
954
955
956
957 static void
958 on_destroy_dialog (GtkDialog *dialog)
959 {
960         gtk_widget_destroy (GTK_WIDGET(dialog));
961         if (gtk_events_pending ())
962                 gtk_main_iteration ();
963 }
964
965 gint
966 modest_platform_run_confirmation_dialog (GtkWindow *parent_window,
967                                          const gchar *message)
968 {
969         GtkWidget *dialog;
970         gint response;
971         
972         dialog = hildon_note_new_confirmation (parent_window, message);
973         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), 
974                                      GTK_WINDOW (dialog));
975
976         response = gtk_dialog_run (GTK_DIALOG (dialog));
977
978         on_destroy_dialog (GTK_DIALOG(dialog));
979         
980         while (gtk_events_pending ())
981                 gtk_main_iteration ();
982
983         return response;
984 }
985         
986 gint
987 modest_platform_run_yes_no_dialog (GtkWindow *parent_window,
988                                    const gchar *message)
989 {
990         GtkWidget *dialog;
991         gint response;
992         
993         dialog = hildon_note_new_confirmation_add_buttons (parent_window, message,
994                                                            _("mcen_bd_yes"), GTK_RESPONSE_YES,
995                                                            _("mcen_bd_no"), GTK_RESPONSE_NO,
996                                                            NULL);
997         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (), GTK_WINDOW (dialog));
998         response = gtk_dialog_run (GTK_DIALOG (dialog));
999         
1000         on_destroy_dialog (GTK_DIALOG(dialog));
1001
1002         while (gtk_events_pending ())
1003                 gtk_main_iteration ();
1004
1005         return response;
1006 }
1007
1008
1009
1010 void
1011 modest_platform_run_information_dialog (GtkWindow *parent_window,
1012                                         const gchar *message)
1013 {
1014         GtkWidget *note;
1015         
1016         note = hildon_note_new_information (parent_window, message);
1017         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1018                                      GTK_WINDOW (note));
1019         
1020         g_signal_connect_swapped (note,
1021                                   "response", 
1022                                   G_CALLBACK (on_destroy_dialog),
1023                                   note);
1024
1025         gtk_widget_show_all (note);
1026 }
1027
1028
1029
1030 typedef struct _ConnectAndWaitData {
1031         GMutex *mutex;
1032         GMainLoop *wait_loop;
1033         gboolean has_callback;
1034         gulong handler;
1035 } ConnectAndWaitData;
1036
1037
1038 static void
1039 quit_wait_loop (TnyAccount *account,
1040                 ConnectAndWaitData *data) 
1041 {
1042         /* Set the has_callback to TRUE (means that the callback was
1043            executed and wake up every code waiting for cond to be
1044            TRUE */
1045         g_mutex_lock (data->mutex);
1046         data->has_callback = TRUE;
1047         if (data->wait_loop)
1048                 g_main_loop_quit (data->wait_loop);
1049         g_mutex_unlock (data->mutex);
1050 }
1051
1052 static void
1053 on_connection_status_changed (TnyAccount *account, 
1054                               TnyConnectionStatus status,
1055                               gpointer user_data)
1056 {
1057         TnyConnectionStatus conn_status;
1058         ConnectAndWaitData *data;
1059                         
1060         /* Ignore if reconnecting or disconnected */
1061         conn_status = tny_account_get_connection_status (account);
1062         if (conn_status == TNY_CONNECTION_STATUS_RECONNECTING ||
1063             conn_status == TNY_CONNECTION_STATUS_DISCONNECTED)
1064                 return;
1065
1066         /* Remove the handler */
1067         data = (ConnectAndWaitData *) user_data;
1068         g_signal_handler_disconnect (account, data->handler);
1069
1070         /* Quit from wait loop */
1071         quit_wait_loop (account, (ConnectAndWaitData *) user_data);
1072 }
1073
1074 static void
1075 on_tny_camel_account_set_online_cb (TnyCamelAccount *account, 
1076                                     gboolean canceled, 
1077                                     GError *err, 
1078                                     gpointer user_data)
1079 {
1080         /* Quit from wait loop */
1081         quit_wait_loop (TNY_ACCOUNT (account), (ConnectAndWaitData *) user_data);
1082 }
1083
1084 gboolean 
1085 modest_platform_connect_and_wait (GtkWindow *parent_window, 
1086                                   TnyAccount *account)
1087 {
1088         ConnectAndWaitData *data = NULL;
1089         gboolean device_online;
1090         TnyDevice *device;
1091         TnyConnectionStatus conn_status;
1092         gboolean user_requested;
1093         
1094         device = modest_runtime_get_device();
1095         device_online = tny_device_is_online (device);
1096
1097         /* Whether the connection is user requested or automatically
1098            requested, for example via D-Bus */
1099         user_requested = (parent_window) ? TRUE : FALSE;
1100
1101         /* If there is no account check only the device status */
1102         if (!account) {
1103                 if (device_online)
1104                         return TRUE;
1105                 else
1106                         return tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1107                                                                NULL, user_requested);
1108         }
1109
1110         /* Return if the account is already connected */
1111         conn_status = tny_account_get_connection_status (account);
1112         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED)
1113                 return TRUE;
1114
1115         /* Create the helper */
1116         data = g_slice_new0 (ConnectAndWaitData);
1117         data->mutex = g_mutex_new ();
1118         data->has_callback = FALSE;
1119
1120         /* Connect the device */
1121         if (!device_online) {
1122                 /* Track account connection status changes */
1123                 data->handler = g_signal_connect (account, "connection-status-changed",                                     
1124                                                   G_CALLBACK (on_connection_status_changed),
1125                                                   data);
1126                 /* Try to connect the device */
1127                 device_online = tny_maemo_conic_device_connect (TNY_MAEMO_CONIC_DEVICE (device), 
1128                                                                 NULL, user_requested);
1129
1130                 /* If the device connection failed then exit */
1131                 if (!device_online && data->handler)
1132                         goto frees;
1133         } else {
1134                 /* Force a reconnection of the account */
1135                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1136                                               on_tny_camel_account_set_online_cb, data);
1137         }
1138
1139         /* Wait until the callback is executed */
1140         g_mutex_lock (data->mutex);
1141         if (!data->has_callback) {
1142                 data->wait_loop = g_main_loop_new (g_main_context_new (), FALSE);
1143                 gdk_threads_leave ();
1144                 g_mutex_unlock (data->mutex);
1145                 g_main_loop_run (data->wait_loop);
1146                 g_mutex_lock (data->mutex);
1147                 gdk_threads_enter ();
1148         }
1149         g_mutex_unlock (data->mutex);
1150
1151  frees:
1152         if (data) {
1153                 if (g_signal_handler_is_connected (account, data->handler))
1154                         g_signal_handler_disconnect (account, data->handler);
1155                 g_mutex_free (data->mutex);
1156                 g_main_loop_unref (data->wait_loop);
1157                 g_slice_free (ConnectAndWaitData, data);
1158         }
1159
1160         conn_status = tny_account_get_connection_status (account);
1161         return (conn_status == TNY_CONNECTION_STATUS_CONNECTED) ? TRUE: FALSE;
1162 }
1163
1164 gboolean 
1165 modest_platform_connect_and_wait_if_network_account (GtkWindow *parent_window, TnyAccount *account)
1166 {
1167         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
1168                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
1169                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
1170                         /* This must be a maildir account, which does not require a connection: */
1171                         return TRUE;
1172                 }
1173         }
1174
1175         return modest_platform_connect_and_wait (parent_window, account);
1176 }
1177
1178 gboolean 
1179 modest_platform_connect_and_wait_if_network_folderstore (GtkWindow *parent_window, TnyFolderStore *folder_store)
1180 {
1181         if (!folder_store)
1182                 return TRUE; /* Maybe it is something local. */
1183                 
1184         gboolean result = TRUE;
1185         if (TNY_IS_FOLDER (folder_store)) {
1186                 /* Get the folder's parent account: */
1187                 TnyAccount *account = tny_folder_get_account(TNY_FOLDER (folder_store));
1188                 if (account != NULL) {
1189                         result = modest_platform_connect_and_wait_if_network_account (NULL, account);
1190                         g_object_unref (account);
1191                 }
1192         } else if (TNY_IS_ACCOUNT (folder_store)) {
1193                 /* Use the folder store as an account: */
1194                 result = modest_platform_connect_and_wait_if_network_account (NULL, TNY_ACCOUNT (folder_store));
1195         }
1196
1197         return result;
1198 }
1199
1200 void
1201 modest_platform_run_sort_dialog (GtkWindow *parent_window,
1202                                  ModestSortDialogType type)
1203 {
1204         GtkWidget *dialog = NULL;
1205
1206         /* Build dialog */
1207         dialog = hildon_sort_dialog_new (parent_window);
1208         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1209                                      GTK_WINDOW (dialog));
1210
1211         hildon_help_dialog_help_enable (GTK_DIALOG(dialog),
1212                                         "applications_email_sort",
1213                                         modest_maemo_utils_get_osso_context());
1214
1215         /* Fill sort keys */
1216         switch (type) {
1217         case MODEST_SORT_HEADERS:
1218                 launch_sort_headers_dialog (parent_window, 
1219                                             HILDON_SORT_DIALOG(dialog));
1220                 break;
1221         }
1222         
1223         /* Free */
1224         on_destroy_dialog (GTK_DIALOG(dialog));
1225 }
1226
1227
1228 gboolean 
1229 modest_platform_set_update_interval (guint minutes)
1230 {
1231 #ifdef MODEST_HAVE_LIBALARM
1232         
1233         ModestConf *conf = modest_runtime_get_conf ();
1234         if (!conf)
1235                 return FALSE;
1236                 
1237         cookie_t alarm_cookie = modest_conf_get_int (conf, MODEST_CONF_ALARM_ID, NULL);
1238
1239         /* Delete any existing alarm,
1240          * because we will replace it: */
1241         if (alarm_cookie) {
1242                 if (alarm_event_del(alarm_cookie) != 1)
1243                         g_warning ("%s: alarm %d was not on the queue", __FUNCTION__, (int)alarm_cookie);
1244                 alarm_cookie = 0;
1245                 modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, 0, NULL);
1246         }
1247         
1248         /* 0 means no updates: */
1249         if (minutes == 0)
1250                 return TRUE;
1251         
1252      
1253         /* Register alarm: */
1254         
1255         /* Set the interval in alarm_event_t structure: */
1256         alarm_event_t *event = g_new0(alarm_event_t, 1);
1257         event->alarm_time = minutes * 60; /* seconds */
1258         
1259         /* Set recurrence every few minutes: */
1260         event->recurrence = minutes;
1261         event->recurrence_count = -1; /* Means infinite */
1262
1263         /* Specify what should happen when the alarm happens:
1264          * It should call this D-Bus method: */
1265          
1266         event->dbus_path = g_strdup(MODEST_DBUS_OBJECT);
1267         event->dbus_interface = g_strdup (MODEST_DBUS_IFACE);
1268         event->dbus_service = g_strdup (MODEST_DBUS_SERVICE);
1269         event->dbus_name = g_strdup (MODEST_DBUS_METHOD_SEND_RECEIVE);
1270
1271         /* Use ALARM_EVENT_NO_DIALOG: Otherwise, a dialog will be shown if 
1272          * exec_name or dbus_path is NULL, even though we have specified no dialog text.
1273          * Also use ALARM_EVENT_ACTIVATION so that modest is started (without UI) to get emails 
1274          * This is why we want to use the Alarm API instead of just g_timeout_add().
1275          * (The old maemo email-client did this, though it isn't specified in the UI spec.)
1276          */
1277         event->flags = ALARM_EVENT_NO_DIALOG | ALARM_EVENT_ACTIVATION;
1278         
1279         alarm_cookie = alarm_event_add (event);
1280
1281         /* now, free it */
1282         alarm_event_free (event);
1283         
1284         /* Store the alarm ID in GConf, so we can remove it later:
1285          * This is apparently valid between application instances. */
1286         modest_conf_set_int (conf, MODEST_CONF_ALARM_ID, alarm_cookie, NULL);
1287         
1288         if (!alarm_cookie) {
1289             /* Error */
1290             const alarm_error_t alarm_error = alarmd_get_error ();
1291             g_debug ("Error setting alarm event. Error code: '%d'\n", alarm_error);
1292             
1293             /* Give people some clue: */
1294             /* The alarm API should have a function for this: */
1295             if (alarm_error == ALARMD_ERROR_DBUS) {
1296                 g_debug ("  ALARMD_ERROR_DBUS: An error with D-Bus occurred, probably coudn't get a D-Bus connection.\n");
1297             } else if (alarm_error == ALARMD_ERROR_CONNECTION) {
1298                 g_debug ("  ALARMD_ERROR_CONNECTION: Could not contact alarmd via D-Bus.\n");
1299             } else if (alarm_error == ALARMD_ERROR_INTERNAL) {
1300                 g_debug ("  ALARMD_ERROR_INTERNAL: Some alarmd or libalarm internal error, possibly a version mismatch.\n");
1301             } else if (alarm_error == ALARMD_ERROR_MEMORY) {
1302                 g_debug ("  ALARMD_ERROR_MEMORY: A memory allocation failed.\n");
1303             } else if (alarm_error == ALARMD_ERROR_ARGUMENT) {
1304                 g_debug ("  ALARMD_ERROR_ARGUMENT: An argument given by caller was invalid.\n");
1305             } else if (alarm_error == ALARMD_ERROR_NOT_RUNNING) {
1306                 g_debug ("  ALARMD_ERROR_NOT_RUNNING: alarmd is not running.\n");
1307             }
1308             
1309             return FALSE;
1310         }
1311 #endif /* MODEST_HAVE_LIBALARM */       
1312         return TRUE;
1313 }
1314
1315 void 
1316 modest_platform_on_new_headers_received (TnyList *header_list) 
1317 {
1318 #ifdef MODEST_HAVE_HILDON_NOTIFY
1319         HildonNotification *notification;
1320         TnyIterator *iter;
1321         GSList *notifications_list = NULL;
1322
1323         /* Get previous notifications ids */
1324         notifications_list = modest_conf_get_list (modest_runtime_get_conf (), 
1325                                                    MODEST_CONF_NOTIFICATION_IDS, 
1326                                                    MODEST_CONF_VALUE_INT, NULL);
1327
1328         iter = tny_list_create_iterator (header_list);
1329         while (!tny_iterator_is_done (iter)) {
1330                 gchar *url = NULL, *display_address = NULL,  *summary = NULL;
1331                 const gchar *display_date;
1332                 TnyHeader *header = TNY_HEADER (tny_iterator_get_current (iter));
1333                 TnyFolder *folder = tny_header_get_folder (header);
1334                 gboolean first_notification = TRUE;
1335                 gint notif_id;
1336
1337                 /* constant string, don't free */
1338                 display_date = modest_text_utils_get_display_date (tny_header_get_date_received (header));
1339
1340                 display_address = g_strdup(tny_header_get_from (header));
1341                 modest_text_utils_get_display_address (display_address); /* string is changed in-place */
1342                 
1343                 summary = g_strdup_printf ("%s - %s", display_date, display_address);
1344                 notification = hildon_notification_new (summary,
1345                                                         tny_header_get_subject (header),
1346                                                         "qgn_list_messagin",
1347                                                         "email.arrive");
1348                 /* Create the message URL */
1349                 url = g_strdup_printf ("%s/%s", tny_folder_get_url_string (folder), 
1350                                        tny_header_get_uid (header));
1351
1352                 hildon_notification_add_dbus_action(notification,
1353                                                     "default",
1354                                                     "Cancel",
1355                                                     MODEST_DBUS_SERVICE,
1356                                                     MODEST_DBUS_OBJECT,
1357                                                     MODEST_DBUS_IFACE,
1358                                                     MODEST_DBUS_METHOD_OPEN_MESSAGE,
1359                                                     G_TYPE_STRING, url,
1360                                                     -1);
1361
1362                 /* Play sound if the user wants. Show the LED
1363                    pattern. Show and play just one */
1364                 if (G_UNLIKELY (first_notification)) {
1365                         first_notification = FALSE;
1366                         if (modest_conf_get_bool (modest_runtime_get_conf (),
1367                                                   MODEST_CONF_PLAY_SOUND_MSG_ARRIVE,
1368                                                   NULL))  {
1369                                 notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1370                                                                     "sound-file", "/usr/share/sounds/ui-new_email.wav");
1371                         }
1372
1373                         /* Set the led pattern */
1374                         notify_notification_set_hint_int32 (NOTIFY_NOTIFICATION (notification),
1375                                                             "dialog-type", 4);
1376                         notify_notification_set_hint_string(NOTIFY_NOTIFICATION (notification),
1377                                                             "led-pattern",
1378                                                             "PatternCommunicationEmail");                       
1379                 }
1380
1381                 /* Notify. We need to do this in an idle because this function
1382                    could be called from a thread */
1383                 notify_notification_show (NOTIFY_NOTIFICATION (notification), NULL);
1384
1385                 /* Save id in the list */
1386                 g_object_get(G_OBJECT(notification), "id", &notif_id, NULL);
1387                 notifications_list = g_slist_prepend (notifications_list, GINT_TO_POINTER(notif_id));
1388                 /* We don't listen for the "closed" signal, because we
1389                    don't care about if the notification was removed or
1390                    not to store the list in gconf */
1391         
1392                 /* Free & carry on */
1393                 g_free (display_address);
1394                 g_free (summary);
1395                 g_free (url);
1396                 g_object_unref (folder);
1397                 g_object_unref (header);
1398                 tny_iterator_next (iter);
1399         }
1400         g_object_unref (iter);
1401
1402         /* Save the ids */
1403         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1404                               notifications_list, MODEST_CONF_VALUE_INT, NULL);
1405
1406         g_slist_free (notifications_list);
1407         
1408 #endif /*MODEST_HAVE_HILDON_NOTIFY*/
1409 }
1410
1411 void
1412 modest_platform_remove_new_mail_notifications (void) 
1413 {
1414 #ifdef MODEST_HAVE_HILDON_NOTIFY
1415         GSList *notif_list = NULL;
1416
1417         /* Get previous notifications ids */
1418         notif_list = modest_conf_get_list (modest_runtime_get_conf (), 
1419                                            MODEST_CONF_NOTIFICATION_IDS, 
1420                                            MODEST_CONF_VALUE_INT, NULL);
1421
1422         while (notif_list) {
1423                 gint notif_id;
1424                 NotifyNotification *notif;
1425
1426                 /* Nasty HACK to remove the notifications, set the id
1427                    of the existing ones and then close them */
1428                 notif_id = GPOINTER_TO_INT(notif_list->data);
1429                 notif = notify_notification_new("dummy", NULL, NULL, NULL);
1430                 g_object_set(G_OBJECT(notif), "id", notif_id, NULL);
1431
1432                 /* Close the notification, note that some ids could be
1433                    already invalid, but we don't care because it does
1434                    not fail */
1435                 notify_notification_close(notif, NULL);
1436                 g_object_unref(notif);
1437
1438                 /* Delete the link, it's like going to the next */
1439                 notif_list = g_slist_delete_link (notif_list, notif_list);
1440         }
1441
1442         /* Save the ids */
1443         modest_conf_set_list (modest_runtime_get_conf (), MODEST_CONF_NOTIFICATION_IDS, 
1444                               notif_list, MODEST_CONF_VALUE_INT, NULL);
1445
1446         g_slist_free (notif_list);
1447
1448 #endif /* MODEST_HAVE_HILDON_NOTIFY */
1449 }
1450
1451
1452
1453 GtkWidget * 
1454 modest_platform_get_global_settings_dialog ()
1455 {
1456         return modest_maemo_global_settings_dialog_new ();
1457 }
1458
1459 void
1460 modest_platform_show_help (GtkWindow *parent_window, 
1461                            const gchar *help_id)
1462 {
1463         osso_return_t result;
1464         g_return_if_fail (help_id);
1465
1466         result = hildon_help_show (modest_maemo_utils_get_osso_context(),
1467                                    help_id, HILDON_HELP_SHOW_DIALOG);
1468         
1469         if (result != OSSO_OK) {
1470                 gchar *error_msg;
1471                 error_msg = g_strdup_printf ("FIXME The help topic %s could not be found", help_id); 
1472                 hildon_banner_show_information (GTK_WIDGET (parent_window),
1473                                                 NULL,
1474                                                 error_msg);
1475                 g_free (error_msg);
1476         }
1477 }
1478
1479 void 
1480 modest_platform_show_search_messages (GtkWindow *parent_window)
1481 {
1482         osso_return_t result = OSSO_ERROR;
1483         
1484         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1485                                              "osso_global_search",
1486                                              "search_email", NULL, DBUS_TYPE_INVALID);
1487
1488         if (result != OSSO_OK) {
1489                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1490         }
1491 }
1492
1493 void 
1494 modest_platform_show_addressbook (GtkWindow *parent_window)
1495 {
1496         osso_return_t result = OSSO_ERROR;
1497         
1498         result = osso_rpc_run_with_defaults (modest_maemo_utils_get_osso_context(),
1499                                              "osso_addressbook",
1500                                              "top_application", NULL, DBUS_TYPE_INVALID);
1501
1502         if (result != OSSO_OK) {
1503                 g_warning ("%s: osso_rpc_run_with_defaults() failed.\n", __FUNCTION__);
1504         }
1505 }
1506
1507 GtkWidget *
1508 modest_platform_create_folder_view (TnyFolderStoreQuery *query)
1509 {
1510         GtkWidget *widget = modest_folder_view_new (query);
1511
1512         /* Show one account by default */
1513         modest_folder_view_set_style (MODEST_FOLDER_VIEW (widget),
1514                                       MODEST_FOLDER_VIEW_STYLE_SHOW_ONE);
1515
1516
1517         /* Restore settings */
1518         modest_widget_memory_restore (modest_runtime_get_conf(), 
1519                                       G_OBJECT (widget),
1520                                       MODEST_CONF_FOLDER_VIEW_KEY);
1521
1522         return widget;
1523 }
1524
1525 void 
1526 modest_platform_information_banner (GtkWidget *parent,
1527                                     const gchar *icon_name,
1528                                     const gchar *text)
1529 {
1530         hildon_banner_show_information (parent, icon_name, text);
1531 }
1532
1533 GtkWidget *
1534 modest_platform_animation_banner (GtkWidget *parent,
1535                                   const gchar *animation_name,
1536                                   const gchar *text)
1537 {
1538         GtkWidget *inf_note = NULL;
1539
1540         g_return_val_if_fail (text != NULL, NULL);
1541
1542         inf_note = hildon_banner_show_animation (parent, animation_name, text);
1543
1544         return inf_note;
1545 }
1546
1547 typedef struct
1548 {
1549         GMainLoop* loop;
1550         TnyAccount *account;
1551         gboolean is_online;
1552         gint count_tries;
1553 } CheckAccountIdleData;
1554
1555 #define NUMBER_OF_TRIES 10 /* Try approx every second, ten times. */
1556
1557 static gboolean 
1558 on_timeout_check_account_is_online(CheckAccountIdleData* data)
1559 {
1560         gboolean stop_trying = FALSE;
1561         g_return_val_if_fail (data && data->account, FALSE);
1562         
1563         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n", __FUNCTION__,
1564                 tny_account_get_connection_status (data->account));     
1565         
1566         if (data && data->account && 
1567                 /* We want to wait until TNY_CONNECTION_STATUS_INIT has changed to something else,
1568                  * after which the account is likely to be usable, or never likely to be usable soon: */
1569                 (tny_account_get_connection_status (data->account) != TNY_CONNECTION_STATUS_INIT) )
1570         {
1571                 data->is_online = TRUE;
1572                 
1573                 stop_trying = TRUE;
1574         } else {
1575                 /* Give up if we have tried too many times: */
1576                 if (data->count_tries >= NUMBER_OF_TRIES) {
1577                         stop_trying = TRUE;
1578                 } else {
1579                         /* Wait for another timeout: */
1580                         ++(data->count_tries);
1581                 }
1582         }
1583         
1584         if (stop_trying) {
1585                 /* Allow the function that requested this idle callback to continue: */
1586                 if (data->loop)
1587                         g_main_loop_quit (data->loop);
1588                         
1589                 if (data->account)
1590                         g_object_unref (data->account);
1591                 
1592                 return FALSE; /* Don't call this again. */
1593         } else {
1594                 return TRUE; /* Call this timeout callback again. */
1595         }
1596 }
1597
1598 /* Return TRUE immediately if the account is already online,
1599  * otherwise check every second for NUMBER_OF_TRIES seconds and return TRUE as 
1600  * soon as the account is online, or FALSE if the account does 
1601  * not become online in the NUMBER_OF_TRIES seconds.
1602  * This is useful when the D-Bus method was run immediately after 
1603  * the application was started (when using D-Bus activation), 
1604  * because the account usually takes a short time to go online.
1605  * The return value is maybe not very useful.
1606  */
1607 gboolean
1608 modest_platform_check_and_wait_for_account_is_online(TnyAccount *account)
1609 {
1610         g_return_val_if_fail (account, FALSE);
1611         
1612         printf ("DEBUG: %s: account id=%s\n", __FUNCTION__, tny_account_get_id (account));
1613         
1614         if (!tny_device_is_online (modest_runtime_get_device())) {
1615                 printf ("DEBUG: %s: device is offline.\n", __FUNCTION__);
1616                 return FALSE;
1617         }
1618         
1619         /* The local_folders account never seems to leave TNY_CONNECTION_STATUS_INIT,
1620          * so we avoid wait unnecessarily: */
1621         if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) && 
1622                 !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account) ) {
1623                 return TRUE;            
1624         }
1625                 
1626         printf ("DEBUG: %s: tny_account_get_connection_status()==%d\n",
1627                 __FUNCTION__, tny_account_get_connection_status (account));
1628         
1629         /* The POP & IMAP store accounts seem to be TNY_CONNECTION_STATUS_DISCONNECTED, 
1630          * and that seems to be an OK time to use them. Maybe it's just TNY_CONNECTION_STATUS_INIT that 
1631          * we want to avoid. */
1632         if (tny_account_get_connection_status (account) != TNY_CONNECTION_STATUS_INIT)
1633                 return TRUE;
1634                 
1635         /* This blocks on the result: */
1636         CheckAccountIdleData *data = g_slice_new0 (CheckAccountIdleData);
1637         data->is_online = FALSE;
1638         data->account = account;
1639         g_object_ref (data->account);
1640         data->count_tries = 0;
1641                 
1642         GMainContext *context = NULL; /* g_main_context_new (); */
1643         data->loop = g_main_loop_new (context, FALSE /* not running */);
1644
1645         g_timeout_add (1000, (GSourceFunc)(on_timeout_check_account_is_online), data);
1646
1647         /* This main loop will run until the idle handler has stopped it: */
1648         g_main_loop_run (data->loop);
1649
1650         g_main_loop_unref (data->loop);
1651         /* g_main_context_unref (context); */
1652
1653         g_slice_free (CheckAccountIdleData, data);
1654         
1655         return data->is_online; 
1656 }
1657
1658
1659
1660 static void
1661 on_cert_dialog_response (GtkDialog *dialog, gint response_id,  const gchar* cert)
1662 {
1663         /* GTK_RESPONSE_HELP means we need to show the certificate */
1664         if (response_id == GTK_RESPONSE_HELP) {
1665                 GtkWidget *note;
1666                 gchar *msg;
1667                 
1668                 /* Do not close the dialog */
1669                 g_signal_stop_emission_by_name (dialog, "response");
1670
1671                 msg = g_strdup_printf (_("mcen_ni_view_unknown_certificate"), cert);    
1672                 note = hildon_note_new_information (GTK_WINDOW(dialog), msg);
1673                 gtk_dialog_run (GTK_DIALOG(note));
1674                 gtk_widget_destroy (note);
1675         }
1676 }
1677
1678
1679 gboolean
1680 modest_platform_run_certificate_confirmation_dialog (const gchar* server_name,
1681                                                      const gchar *certificate)
1682 {
1683         GtkWidget *note;
1684         gint response;
1685         ModestWindow *main_win;
1686         
1687         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1688                 g_warning ("%s: don't show dialogs if there's no main window; assuming 'Cancel'",
1689                            __FUNCTION__);
1690                 return FALSE;
1691         }
1692
1693         /* don't create it */
1694         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr(), FALSE);
1695         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1696         
1697         
1698         gchar *question = g_strdup_printf (_("mcen_nc_unknown_certificate"),
1699                                            server_name);
1700         
1701         note = hildon_note_new_confirmation_add_buttons  (
1702                 GTK_WINDOW(main_win),
1703                 question,
1704                 _("mcen_bd_dialog_ok"),     GTK_RESPONSE_OK,
1705                 _("mcen_bd_view"),          GTK_RESPONSE_HELP,   /* abusing this... */
1706                 _("mcen_bd_dialog_cancel"), GTK_RESPONSE_CANCEL,
1707                 NULL, NULL);
1708         
1709         g_signal_connect (G_OBJECT(note), "response", 
1710                           G_CALLBACK(on_cert_dialog_response),
1711                           (gpointer) certificate);
1712         
1713         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1714                                      GTK_WINDOW (note));
1715         response = gtk_dialog_run(GTK_DIALOG(note));
1716
1717         on_destroy_dialog (GTK_DIALOG(note));
1718         g_free (question);
1719         
1720         return response == GTK_RESPONSE_OK;
1721 }
1722
1723 gboolean
1724 modest_platform_run_alert_dialog (const gchar* prompt, 
1725                                   gboolean is_question)
1726 {       
1727         ModestWindow *main_win; 
1728
1729         if (!modest_window_mgr_main_window_exists (modest_runtime_get_window_mgr())) {
1730                 g_warning ("%s:\n'%s'\ndon't show dialogs if there's no main window;"
1731                            " assuming 'Cancel' for questions, 'Ok' otherwise", prompt, __FUNCTION__);
1732                 return is_question ? FALSE : TRUE;
1733         }
1734
1735         main_win = modest_window_mgr_get_main_window (modest_runtime_get_window_mgr (), FALSE);
1736         g_return_val_if_fail (main_win, FALSE); /* should not happen */
1737         
1738         gboolean retval = TRUE;
1739         if (is_question) {
1740                 /* The Tinymail documentation says that we should show Yes and No buttons, 
1741                  * when it is a question.
1742                  * Obviously, we need tinymail to use more specific error codes instead,
1743                  * so we know what buttons to show. */
1744                 GtkWidget *dialog = GTK_WIDGET (hildon_note_new_confirmation (GTK_WINDOW (main_win), 
1745                                                                               prompt));
1746                 modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
1747                                              GTK_WINDOW (dialog));
1748                 
1749                 const int response = gtk_dialog_run (GTK_DIALOG (dialog));
1750                 retval = (response == GTK_RESPONSE_YES) || (response == GTK_RESPONSE_OK);
1751                 
1752                 on_destroy_dialog (GTK_DIALOG(dialog));         
1753         } else {
1754                 /* Just show the error text and use the default response: */
1755                 modest_platform_run_information_dialog (GTK_WINDOW (main_win), 
1756                                                         prompt);
1757         }
1758         return retval;
1759 }
1760
1761 /***************/
1762 typedef struct {
1763         GtkWindow *parent_window;
1764         ModestConnectedPerformer callback;
1765         TnyAccount *account;
1766         gpointer user_data;
1767         gchar *iap;
1768         TnyDevice *device;
1769 } OnWentOnlineInfo;
1770  
1771 static void 
1772 on_went_online_info_free (OnWentOnlineInfo *info)
1773 {
1774         /* And if we cleanup, we DO cleanup  :-)  */
1775         
1776         if (info->device)
1777                 g_object_unref (info->device);
1778         if (info->iap)
1779                 g_free (info->iap);
1780         if (info->parent_window)
1781                 g_object_unref (info->parent_window);
1782         if (info->account)
1783                 g_object_unref (info->account);
1784         
1785         g_slice_free (OnWentOnlineInfo, info);
1786         
1787         /* We're done ... */
1788         
1789         return;
1790 }
1791  
1792 static void
1793 on_account_went_online (TnyCamelAccount *account, gboolean canceled, GError *err, gpointer user_data)
1794 {
1795         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1796  
1797         /* Now it's really time to callback to the caller. If going online didn't succeed,
1798          * err will be set. We don't free it, Tinymail does that! If a cancel happened,
1799          * canceled will be set. Etcetera etcetera. */
1800         
1801         if (info->callback) {
1802                 info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1803         }
1804         
1805         /* This is our last call, we must cleanup here if we didn't yet do that */
1806         on_went_online_info_free (info);
1807         
1808         return;
1809 }
1810  
1811  
1812 static void
1813 on_conic_device_went_online (TnyMaemoConicDevice *device, const gchar* iap_id, gboolean canceled, GError *err, gpointer user_data)
1814 {
1815         OnWentOnlineInfo *info = (OnWentOnlineInfo *) user_data;
1816         info->iap = g_strdup (iap_id);
1817         
1818         if (canceled || err || !info->account) {
1819         
1820                 /* If there's a problem or if there's no account (then that's it for us, we callback
1821                  * the caller's callback now. He'll have to handle err or canceled, of course.
1822                  * We are not really online, as the account is not really online here ... */    
1823                 
1824                 /* We'll use the err and the canceled of this cb. TnyMaemoConicDevice delivered us
1825                  * this info. We don't cleanup err, Tinymail does that! */
1826                 
1827                 if (info->callback) {
1828                         
1829                         /* info->account can be NULL here, this means that the user did not
1830                          * provide a nice account instance. We'll assume that the user knows
1831                          * what he's doing and is happy with just the device going online. 
1832                          * 
1833                          * We can't do magic, we don't know what account the user wants to
1834                          * see going online. So just the device goes online, end of story */
1835                         
1836                         info->callback (canceled, err, info->parent_window, info->account, info->user_data);
1837                 }
1838                 
1839         } else if (info->account) {
1840                 
1841                 /* If there's no problem and if we have an account, we'll put the account
1842                  * online too. When done, the callback of bringing the account online
1843                  * will callback the caller's callback. This is the most normal case. */
1844  
1845                 info->device = TNY_DEVICE (g_object_ref (device));
1846                 
1847                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (info->account), TRUE,
1848                                               on_account_went_online, info);
1849                 
1850                 /* The on_account_went_online cb frees up the info, go look if you
1851                  * don't believe me! (so we return here) */
1852                 
1853                 return;
1854         }
1855         
1856         /* We cleanup if we are not bringing the account online too */
1857         on_went_online_info_free (info);
1858  
1859         return; 
1860 }
1861         
1862 void 
1863 modest_platform_connect_and_perform (GtkWindow *parent_window, 
1864                                      TnyAccount *account, 
1865                                      ModestConnectedPerformer callback, 
1866                                      gpointer user_data)
1867 {
1868         gboolean device_online;
1869         TnyDevice *device;
1870         TnyConnectionStatus conn_status;
1871         OnWentOnlineInfo *info;
1872         gboolean user_requested;
1873         
1874         device = modest_runtime_get_device();
1875         device_online = tny_device_is_online (device);
1876
1877         /* Whether the connection is user requested or automatically
1878            requested, for example via D-Bus */
1879         user_requested = (parent_window) ? TRUE : FALSE;
1880
1881         /* If there is no account check only the device status */
1882         if (!account) {
1883                 
1884                 if (device_online) {
1885  
1886                         /* We promise to instantly perform the callback, so ... */
1887                         if (callback) {
1888                                 callback (FALSE, NULL, parent_window, account, user_data);
1889                         }
1890                         
1891                 } else {
1892                         
1893                         info = g_slice_new0 (OnWentOnlineInfo);
1894                         
1895                         info->iap = NULL;
1896                         info->device = NULL;
1897                         info->account = NULL;
1898                 
1899                         if (parent_window)
1900                                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1901                         else
1902                                 info->parent_window = NULL;
1903                         info->user_data = user_data;
1904                         info->callback = callback;
1905                 
1906                         tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1907                                                               user_requested, on_conic_device_went_online, 
1908                                                               info);
1909  
1910                         /* We'll cleanup in on_conic_device_went_online */
1911                 }
1912  
1913                 /* The other code has no more reason to run. This is all that we can do for the
1914                  * caller (he should have given us a nice and clean account instance!). We
1915                  * can't do magic, we don't know what account he intends to bring online. So
1916                  * we'll just bring the device online (and await his false bug report). */
1917                 
1918                 return;
1919         }
1920  
1921         
1922         /* Return if the account is already connected */
1923         
1924         conn_status = tny_account_get_connection_status (account);
1925         if (device_online && conn_status == TNY_CONNECTION_STATUS_CONNECTED) {
1926  
1927                 /* We promise to instantly perform the callback, so ... */
1928                 if (callback) {
1929                         callback (FALSE, NULL, parent_window, account, user_data);
1930                 }
1931                 
1932                 return;
1933         }
1934         
1935         /* Else, we are in a state that requires that we go online before we
1936          * call the caller's callback. */
1937         
1938         info = g_slice_new0 (OnWentOnlineInfo);
1939         
1940         info->device = NULL;
1941         info->iap = NULL;
1942         info->account = TNY_ACCOUNT (g_object_ref (account));
1943         
1944         if (parent_window)
1945                 info->parent_window = (GtkWindow *) g_object_ref (parent_window);
1946         else
1947                 info->parent_window = NULL;
1948         
1949         /* So we'll put the callback away for later ... */
1950         
1951         info->user_data = user_data;
1952         info->callback = callback;
1953         
1954         if (!device_online) {
1955  
1956                 /* If also the device is offline, then we connect both the device 
1957                  * and the account */
1958                 
1959                 tny_maemo_conic_device_connect_async (TNY_MAEMO_CONIC_DEVICE (device), NULL,
1960                                                       user_requested, on_conic_device_went_online, 
1961                                                       info);
1962                 
1963         } else {
1964                 
1965                 /* If the device is online, we'll just connect the account */
1966                 
1967                 tny_camel_account_set_online (TNY_CAMEL_ACCOUNT (account), TRUE, 
1968                                               on_account_went_online, info);
1969         }
1970  
1971         /* The info gets freed by on_account_went_online or on_conic_device_went_online
1972          * in both situations, go look if you don't believe me! */
1973         
1974         return;
1975 }
1976
1977 void
1978 modest_platform_connect_if_remote_and_perform (GtkWindow *parent_window, 
1979                                                             TnyFolderStore *folder_store, 
1980                                                             ModestConnectedPerformer callback, 
1981                                                             gpointer user_data)
1982 {
1983         TnyAccount *account = NULL;
1984         
1985         if (!folder_store) {
1986                 /* We promise to instantly perform the callback, so ... */
1987                 if (callback) {
1988                         callback (FALSE, NULL, parent_window, NULL, user_data);
1989                 }
1990                 return; 
1991                 
1992                 /* Original comment: Maybe it is something local. */
1993                 /* PVH's comment: maybe we should KNOW this in stead of assuming? */
1994                 
1995         } else if (TNY_IS_FOLDER (folder_store)) {
1996                 /* Get the folder's parent account: */
1997                 account = tny_folder_get_account(TNY_FOLDER (folder_store));
1998         } else if (TNY_IS_ACCOUNT (folder_store)) {
1999                 /* Use the folder store as an account: */
2000                 account = TNY_ACCOUNT (folder_store);
2001         }
2002  
2003         if (tny_account_get_account_type (account) == TNY_ACCOUNT_TYPE_STORE) {
2004                 if (!TNY_IS_CAMEL_POP_STORE_ACCOUNT (account) &&
2005                     !TNY_IS_CAMEL_IMAP_STORE_ACCOUNT (account)) {
2006                         
2007                         /* This IS a local account like a maildir account, which does not require 
2008                          * a connection. (original comment had a vague assumption in its language
2009                          * usage. There's no assuming needed, this IS what it IS: a local account), */
2010  
2011                         /* We promise to instantly perform the callback, so ... */
2012                         if (callback) {
2013                                 callback (FALSE, NULL, parent_window, account, user_data);
2014                         }
2015                         
2016                         return;
2017                 }
2018         }
2019  
2020         modest_platform_connect_and_perform (parent_window, account, callback, user_data);
2021  
2022         return;
2023 }