Fixes NB#130151, remove glib deprecated symbols. There was one missing
[modest] / src / modest-utils.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 <glib.h>
31 #include <glib/gstdio.h>
32 #include <errno.h>
33 #include <string.h> /* for strlen */
34 #include <modest-runtime.h>
35 #include <libgnomevfs/gnome-vfs.h>
36 #include <tny-fs-stream.h>
37 #include <tny-camel-account.h>
38 #include <tny-status.h>
39 #include <tny-camel-send-queue.h>
40 #include <tny-camel-transport-account.h>
41 #include <tny-camel-imap-store-account.h>
42 #include <tny-camel-pop-store-account.h>
43 #include <locale.h>
44 #include <modest-defs.h>
45 #include "modest-utils.h"
46 #include "modest-platform.h"
47 #include <modest-account-protocol.h>
48 #include "modest-account-mgr-helpers.h"
49 #include "modest-text-utils.h"
50 #include <modest-local-folder-info.h>
51 #include "widgets/modest-header-view.h"
52 #include "widgets/modest-main-window.h"
53 #include "modest-widget-memory.h"
54 #include "widgets/modest-sort-criterium-view.h"
55 #ifdef MODEST_TOOLKIT_HILDON2
56 #include "modest-header-window.h"
57 #endif
58 #include <langinfo.h>
59
60 GQuark
61 modest_utils_get_supported_secure_authentication_error_quark (void)
62 {
63         return g_quark_from_static_string("modest-utils-get-supported-secure-authentication-error-quark");
64 }
65
66 gboolean
67 modest_utils_folder_writable (const gchar *filename)
68 {
69         g_return_val_if_fail (filename, FALSE);
70
71         if (!filename)
72                 return FALSE;
73
74         if (g_ascii_strncasecmp (filename, "obex", 4) != 0) {
75                 GnomeVFSFileInfo *folder_info = NULL;
76                 GnomeVFSResult result = GNOME_VFS_OK;
77                 GnomeVFSURI *uri = NULL;
78                 GnomeVFSURI *folder_uri = NULL;
79
80                 uri = gnome_vfs_uri_new (filename);
81                 folder_uri = gnome_vfs_uri_get_parent (uri);
82
83                 if (folder_uri != NULL) {
84                         folder_info = gnome_vfs_file_info_new ();
85                         result = gnome_vfs_get_file_info_uri (folder_uri, folder_info,
86                                                               GNOME_VFS_FILE_INFO_GET_ACCESS_RIGHTS);
87                         gnome_vfs_uri_unref (folder_uri);
88                 }
89                 gnome_vfs_uri_unref (uri);
90
91                 if (folder_uri == NULL)
92                         return FALSE;
93
94                 if ((result != GNOME_VFS_OK) ||
95                     (!((folder_info->permissions & GNOME_VFS_PERM_ACCESS_WRITABLE) ||
96                        (folder_info->permissions & GNOME_VFS_PERM_USER_WRITE)))) {
97
98                         gnome_vfs_file_info_unref (folder_info);
99                         return FALSE;
100                 }
101                 gnome_vfs_file_info_unref (folder_info);
102         }
103         return TRUE;
104 }
105
106 gboolean
107 modest_utils_file_exists (const gchar *filename)
108 {
109         GnomeVFSURI *uri = NULL;
110         gboolean result = FALSE;
111
112         g_return_val_if_fail (filename, FALSE);
113
114         uri = gnome_vfs_uri_new (filename);
115         if (uri) {
116                 result = gnome_vfs_uri_exists (uri);
117                 gnome_vfs_uri_unref (uri);
118         }
119         return result;
120 }
121
122 TnyFsStream *
123 modest_utils_create_temp_stream (const gchar *orig_name, const gchar *hash_base, gchar **path)
124 {
125         gint fd;
126         gchar *filepath = NULL;
127         gchar *tmpdir;
128         guint hash_number;
129
130         /* hmmm... maybe we need a modest_text_utils_validate_file_name? */
131         g_return_val_if_fail (orig_name && strlen(orig_name) != 0, NULL);
132
133         if (strlen(orig_name) > 200) {
134                 g_warning ("%s: filename too long ('%s')",
135                            __FUNCTION__, orig_name);
136                 return NULL;
137         }
138
139         if (g_strstr_len (orig_name, strlen(orig_name), "/") != NULL) {
140                 g_warning ("%s: filename contains '/' character(s) (%s)",
141                            __FUNCTION__, orig_name);
142                 return NULL;
143         }
144
145         /* make a random subdir under /tmp or /var/tmp */
146         if (hash_base != NULL) {
147                 hash_number = g_str_hash (hash_base);
148         } else {
149                 hash_number = (guint) random ();
150         }
151         tmpdir = g_strdup_printf ("%s/%u", g_get_tmp_dir (), hash_number);
152         if ((g_access (tmpdir, R_OK) == -1) && (g_mkdir (tmpdir, 0755) == -1)) {
153                 g_warning ("%s: failed to create dir '%s': %s",
154                            __FUNCTION__, tmpdir, g_strerror(errno));
155                 g_free (tmpdir);
156                 return NULL;
157         }
158
159         filepath = g_strconcat (tmpdir, "/", orig_name, NULL);
160
161         /* if file exists, first we try to remove it */
162         if (modest_utils_file_exists (filepath)) {
163                 g_unlink (filepath);
164         }
165
166         /* don't overwrite if it already exists, even if it is writable */
167         if (modest_utils_file_exists (filepath)) {
168                 if (path!=NULL) {
169                         *path = filepath;
170                 } else {
171                         g_free (filepath);
172                 }
173                 g_free (tmpdir);
174                 return NULL;
175         } else {
176                 /* try to write the file there */
177                 fd = g_open (filepath, O_CREAT|O_WRONLY|O_TRUNC, 0644);
178                 if (fd == -1) {
179                         g_warning ("%s: failed to create '%s': %s",
180                                         __FUNCTION__, filepath, g_strerror(errno));
181                         g_free (filepath);
182                         g_free (tmpdir);
183                         return NULL;
184                 }
185         }
186
187         g_free (tmpdir);
188
189         if (path)
190                 *path = filepath;
191         else
192                 g_free (filepath);
193
194         return TNY_FS_STREAM (tny_fs_stream_new (fd));
195 }
196
197 typedef struct 
198 {
199         GList **result;
200         GtkWidget* dialog;
201         GtkWidget* progress;
202 } ModestGetSupportedAuthInfo;
203
204 static gboolean
205 on_idle_secure_auth_finished (gpointer user_data)
206 {
207         /* Operation has finished, close the dialog. Control continues after
208          * gtk_dialog_run in modest_utils_get_supported_secure_authentication_methods() */
209         gdk_threads_enter(); /* CHECKED */
210         gtk_dialog_response (GTK_DIALOG (user_data), GTK_RESPONSE_ACCEPT);
211         gdk_threads_leave(); /* CHECKED */
212
213         return FALSE;
214 }
215
216 static void
217 on_camel_account_get_supported_secure_authentication (TnyCamelAccount *self,
218                                                       gboolean cancelled,
219                                                       TnyList *auth_types,
220                                                       GError *err,
221                                                       gpointer user_data)
222 {
223         ModestPairList *pairs;
224         GList *result;
225         ModestProtocolRegistry *protocol_registry;
226         ModestGetSupportedAuthInfo *info = (ModestGetSupportedAuthInfo*)user_data;
227         TnyIterator* iter;
228
229         g_return_if_fail (user_data);
230         g_return_if_fail (TNY_IS_CAMEL_ACCOUNT(self));
231         g_return_if_fail (TNY_IS_LIST(auth_types));
232
233         info = (ModestGetSupportedAuthInfo *) user_data;
234
235         /* Free everything if the actual action was canceled */
236         if (cancelled) {
237                 g_debug ("%s: operation canceled\n", __FUNCTION__);
238                 goto close_dialog;
239         }
240
241         if (err) {
242                 g_debug ("%s: error getting the supported auth methods\n", __FUNCTION__);
243                 goto close_dialog;
244         }
245
246         if (!auth_types) {
247                 g_debug ("%s: auth_types is NULL.\n", __FUNCTION__);
248                 goto close_dialog;
249         }
250
251         if (tny_list_get_length(auth_types) == 0) {
252                 g_debug ("%s: auth_types is an empty TnyList.\n", __FUNCTION__);
253                 goto close_dialog;
254         }
255
256         protocol_registry = modest_runtime_get_protocol_registry ();
257         pairs = modest_protocol_registry_get_pair_list_by_tag (protocol_registry, MODEST_PROTOCOL_REGISTRY_AUTH_PROTOCOLS);
258
259         /* Get the enum value for the strings: */
260         result = NULL;
261         iter = tny_list_create_iterator(auth_types);
262         while (!tny_iterator_is_done(iter)) {
263                 TnyPair *pair;
264                 const gchar *auth_name;
265                 ModestProtocolType protocol_type;
266
267                 pair = TNY_PAIR(tny_iterator_get_current(iter));
268                 auth_name = NULL;
269                 if (pair) {
270                         auth_name = tny_pair_get_name(pair);
271                         g_object_unref (pair);
272                         pair = NULL;
273                 }
274
275                 g_debug ("%s: auth_name=%s\n", __FUNCTION__, auth_name);
276
277                 protocol_type = modest_protocol_get_type_id (modest_protocol_registry_get_protocol_by_name (protocol_registry,
278                                                                                                             MODEST_PROTOCOL_REGISTRY_AUTH_PROTOCOLS,
279                                                                                                             auth_name));
280
281                 if (modest_protocol_registry_protocol_type_is_secure (protocol_registry, protocol_type))
282                         result = g_list_prepend(result, GINT_TO_POINTER(protocol_type));
283
284                 tny_iterator_next(iter);
285         }
286         g_object_unref (iter);
287
288         modest_pair_list_free (pairs);
289         *(info->result) = result;
290
291  close_dialog:
292         /* Close the dialog in a main thread */
293         g_idle_add(on_idle_secure_auth_finished, info->dialog);
294
295         /* Free the info */
296         g_slice_free (ModestGetSupportedAuthInfo, info);
297 }
298
299 typedef struct {
300         GtkWidget *progress;
301         gboolean not_finished;
302 } KeepPulsing;
303
304 static gboolean
305 keep_pulsing (gpointer user_data)
306 {
307         KeepPulsing *info = (KeepPulsing *) user_data;
308
309         if (!info->not_finished) {
310                 g_slice_free (KeepPulsing, info);
311                 return FALSE;
312         }
313
314         gtk_progress_bar_pulse (GTK_PROGRESS_BAR (info->progress));
315         return TRUE;
316 }
317
318 GList*
319 modest_utils_get_supported_secure_authentication_methods (ModestProtocolType protocol_type,
320                                                           const gchar* hostname,
321                                                           gint port,
322                                                           const gchar* username,
323                                                           GtkWindow *parent_window,
324                                                           GError** error)
325 {
326         TnyAccount * tny_account = NULL;
327         ModestProtocolRegistry *protocol_registry;
328         GtkWidget *dialog;
329         gint retval;
330         ModestTnyAccountStore *account_store;
331         TnySessionCamel *session = NULL;
332         ModestProtocol *protocol = NULL;
333         GList *result = NULL;
334         GtkWidget *progress;
335
336         g_return_val_if_fail (protocol_type != MODEST_PROTOCOL_REGISTRY_TYPE_INVALID, NULL);
337
338         protocol_registry = modest_runtime_get_protocol_registry ();
339
340         /* We need a connection to get the capabilities; */
341         if (!modest_platform_connect_and_wait (GTK_WINDOW (parent_window), NULL))
342                 return NULL;
343
344         /* Create a TnyCamelAccount so we can use 
345          * tny_camel_account_get_supported_secure_authentication(): */
346         protocol = modest_protocol_registry_get_protocol_by_type (protocol_registry, protocol_type);
347         tny_account = NULL;
348         if (MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
349                 tny_account = modest_account_protocol_create_account (MODEST_ACCOUNT_PROTOCOL (protocol));
350         }
351
352         if (!tny_account) {
353                 g_printerr ("%s could not create tny account.", __FUNCTION__);
354                 return NULL;
355         }
356
357         /* Set proto, so that the prepare_func() vfunc will work when
358          * we call set_session(): */
359         protocol = modest_protocol_registry_get_protocol_by_type (protocol_registry, protocol_type);
360         tny_account_set_id (tny_account, "temp_account");
361         tny_account_set_proto (tny_account, modest_protocol_get_name (protocol));
362         tny_account_set_hostname (tny_account, hostname);
363         tny_account_set_user (tny_account, username);
364
365         if(port > 0)
366                 tny_account_set_port (tny_account, port);
367
368         /* Set the session for the account, so we can use it: */
369         account_store = modest_runtime_get_account_store ();
370         session = modest_tny_account_store_get_session (TNY_ACCOUNT_STORE (account_store));
371         g_return_val_if_fail (session, NULL);
372         tny_camel_account_set_session (TNY_CAMEL_ACCOUNT(tny_account), session);
373
374         dialog = gtk_dialog_new_with_buttons(" ",
375                                              parent_window,
376                                              GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
377                                              _("mcen_bd_dialog_cancel"),
378                                              GTK_RESPONSE_REJECT,
379                                              NULL);
380
381         /* Ask camel to ask the server, asynchronously: */
382         ModestGetSupportedAuthInfo *info = g_slice_new (ModestGetSupportedAuthInfo);
383         info->result = &result;
384         info->dialog = dialog;
385
386         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(info->dialog)->vbox),
387                           gtk_label_new(_("emev_ni_checking_supported_auth_methods")));
388         progress = gtk_progress_bar_new();
389         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(info->dialog)->vbox), progress);
390         gtk_widget_show_all(info->dialog);
391
392         KeepPulsing *pi = g_slice_new (KeepPulsing);
393         pi->progress = progress;
394         pi->not_finished = TRUE;
395
396         /* Starts the pulsing of the progressbar */
397         g_timeout_add (500, keep_pulsing, pi);
398
399         tny_camel_account_get_supported_secure_authentication (TNY_CAMEL_ACCOUNT (tny_account),
400                                                                on_camel_account_get_supported_secure_authentication,
401                                                                NULL,
402                                                                info);
403
404         retval = gtk_dialog_run (GTK_DIALOG (info->dialog));
405
406         pi->not_finished = FALSE;
407         /* pi is freed in the timeout itself to avoid a GCond here */
408
409         gtk_widget_destroy(dialog);
410
411         /* Frees */
412         tny_account_cancel (tny_account);
413         g_object_unref (tny_account);
414
415         return result;
416 }
417
418 void
419 modest_utils_show_dialog_and_forget (GtkWindow *parent_window,
420                                      GtkDialog *dialog)
421 {
422         g_return_if_fail (GTK_IS_WINDOW(parent_window));
423         g_return_if_fail (GTK_IS_DIALOG(dialog));
424
425         gtk_window_set_transient_for (GTK_WINDOW (dialog), parent_window);
426
427         /* Destroy the dialog when it is closed: */
428         g_signal_connect_swapped (dialog,
429                                   "response",
430                                   G_CALLBACK (gtk_widget_destroy),
431                                   dialog);
432
433         gtk_widget_show (GTK_WIDGET (dialog));
434 }
435
436 void
437 modest_utils_toggle_action_set_active_block_notify (GtkToggleAction *action, gboolean value)
438 {
439         GSList *proxies = NULL;
440
441         g_return_if_fail (GTK_IS_TOGGLE_ACTION (action));
442
443         for (proxies = gtk_action_get_proxies (GTK_ACTION (action));
444              proxies != NULL; proxies = g_slist_next (proxies)) {
445                 GtkWidget *widget = (GtkWidget *) proxies->data;
446                 gtk_action_block_activate_from (GTK_ACTION (action), widget);
447         }
448
449         gtk_toggle_action_set_active (action, value);
450
451         for (proxies = gtk_action_get_proxies (GTK_ACTION (action));
452              proxies != NULL; proxies = g_slist_next (proxies)) {
453                 GtkWidget *widget = (GtkWidget *) proxies->data;
454                 gtk_action_unblock_activate_from (GTK_ACTION (action), widget);
455         }
456
457 }
458
459
460 gint 
461 modest_list_index (TnyList *list, GObject *object)
462 {
463         TnyIterator *iter;
464         gint index = 0;
465
466         g_return_val_if_fail (TNY_IS_LIST(list), -1);
467         g_return_val_if_fail (G_IS_OBJECT(object), -1);
468         
469         iter = tny_list_create_iterator (list);
470         while (!tny_iterator_is_done (iter)) {
471                 GObject *current = tny_iterator_get_current (iter);
472
473                 g_object_unref (current);
474                 if (current == object)
475                         break;
476
477                 tny_iterator_next (iter);
478                 index++;
479         }
480
481         if (tny_iterator_is_done (iter))
482                 index = -1;
483         g_object_unref (iter);
484         return index;
485 }
486
487 guint64 
488 modest_utils_get_available_space (const gchar *maildir_path)
489 {
490         gchar *folder;
491         gchar *uri_string;
492         GnomeVFSURI *uri;
493         GnomeVFSFileSize size;
494
495         folder = modest_local_folder_info_get_maildir_path (maildir_path);
496         uri_string = gnome_vfs_get_uri_from_local_path (folder);
497         uri = gnome_vfs_uri_new (uri_string);
498         g_free (folder);
499         g_free (uri_string);
500
501         if (uri) {
502                 if (gnome_vfs_get_volume_free_space (uri, &size) != GNOME_VFS_OK)
503                         size = 0;
504                 gnome_vfs_uri_unref (uri);
505         } else {
506                 size = 0;
507         }
508
509         return (guint64) size;
510 }
511 static void
512 on_destroy_dialog (GtkDialog *dialog)
513 {
514         gtk_widget_destroy (GTK_WIDGET(dialog));
515         if (gtk_events_pending ())
516                 gtk_main_iteration ();
517 }
518
519 static guint
520 checked_modest_sort_criterium_view_add_sort_key (ModestSortCriteriumView *view, const gchar* key, guint max)
521 {
522         gint sort_key;
523         
524         g_return_val_if_fail (view && MODEST_IS_SORT_CRITERIUM_VIEW(view), 0);
525         g_return_val_if_fail (key, 0);
526         
527         sort_key = modest_sort_criterium_view_add_sort_key (view, key);
528         if (sort_key < 0 || sort_key >= max) {
529                 g_warning ("%s: out of range (%d) for %s", __FUNCTION__, sort_key, key);
530                 return 0;
531         } else
532                 return (guint)sort_key; 
533 }
534
535 static void
536 launch_sort_headers_dialog (GtkWindow *parent_window,
537                             GtkDialog *dialog)
538 {
539         ModestHeaderView *header_view = NULL;
540         GList *cols = NULL;
541         GtkSortType sort_type;
542         gint sort_key;
543         gint default_key = 0;
544         gint result;
545         gboolean outgoing = FALSE;
546         gint current_sort_colid = -1;
547         GtkSortType current_sort_type;
548         gint attachments_sort_id;
549         gint priority_sort_id;
550         GtkTreeSortable *sortable;
551         
552         /* Get header window */
553         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
554                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
555                                                                                       MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
556 #ifdef MODEST_TOOLKIT_HILDON2
557         } else if (MODEST_IS_HEADER_WINDOW (parent_window)) {
558                 header_view = MODEST_HEADER_VIEW (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
559 #endif
560
561         }
562         if (!header_view)
563                 return;
564         
565         /* Add sorting keys */
566         cols = modest_header_view_get_columns (header_view);
567         if (cols == NULL) 
568                 return;
569 #define SORT_ID_NUM 6
570         int sort_model_ids[SORT_ID_NUM];
571         int sort_ids[SORT_ID_NUM];
572
573         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
574                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
575
576         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_sender_recipient"),
577                                                                     SORT_ID_NUM);
578         if (outgoing) {
579                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
580                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
581         } else {
582                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
583                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
584         }
585
586         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_date"),
587                                                             SORT_ID_NUM);
588         if (outgoing) {
589                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
590                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
591         } else {
592                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
593                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
594         }
595         default_key = sort_key;
596
597         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_subject"),
598                                                                     SORT_ID_NUM);
599         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
600         if (outgoing)
601                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
602         else
603                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
604
605         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_attachment"),
606                                                                     SORT_ID_NUM);
607         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
608         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
609         attachments_sort_id = sort_key;
610
611         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_size"),
612                                                                     SORT_ID_NUM);
613         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
614         sort_ids[sort_key] = 0;
615
616         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_priority"),
617                                                                     SORT_ID_NUM);
618         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
619         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
620         priority_sort_id = sort_key;
621         
622         sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)));
623         /* Launch dialogs */
624         if (!gtk_tree_sortable_get_sort_column_id (sortable,
625                                                    &current_sort_colid, &current_sort_type)) {
626                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), default_key);
627                 modest_sort_criterium_view_set_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog), GTK_SORT_DESCENDING);
628         } else {
629                 modest_sort_criterium_view_set_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog), current_sort_type);
630                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
631                         gpointer flags_sort_type_pointer;
632                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
633                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
634                                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), priority_sort_id);
635                         else
636                                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), attachments_sort_id);
637                 } else {
638                         gint current_sort_keyid = 0;
639                         while (current_sort_keyid < SORT_ID_NUM) {
640                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
641                                         break;
642                                 else 
643                                         current_sort_keyid++;
644                         }
645                         modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), current_sort_keyid);
646                 }
647         }
648
649         result = gtk_dialog_run (GTK_DIALOG (dialog));
650         if (result == GTK_RESPONSE_OK) {
651                 sort_key = modest_sort_criterium_view_get_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog));
652                 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
653                         g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
654                         sort_key = 0;
655                 }
656
657                 sort_type = modest_sort_criterium_view_get_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog));
658                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
659                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
660                                            GINT_TO_POINTER (sort_ids[sort_key]));
661                         /* This is a hack to make it resort rows always when flag fields are
662                          * selected. If we do not do this, changing sort field from priority to
663                          * attachments does not work */
664                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
665                 } else {
666                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
667                                                                  sort_model_ids[sort_key]);
668                 }
669
670                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
671                 gtk_tree_sortable_sort_column_changed (sortable);
672         }
673
674         modest_widget_memory_save (modest_runtime_get_conf (),
675                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
676         
677         /* free */
678         g_list_free(cols);      
679 }
680
681 void
682 modest_utils_run_sort_dialog (GtkWindow *parent_window,
683                               ModestSortDialogType type)
684 {
685         GtkWidget *dialog = NULL;
686
687         /* Build dialog */
688         dialog = modest_platform_create_sort_dialog (parent_window);
689         if (dialog == NULL)
690                 return;
691         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
692                                      GTK_WINDOW (dialog), parent_window);
693
694         /* Fill sort keys */
695         switch (type) {
696         case MODEST_SORT_HEADERS:
697                 launch_sort_headers_dialog (parent_window, 
698                                             GTK_DIALOG(dialog));
699                 break;
700         }
701         
702         /* Free */
703         on_destroy_dialog (GTK_DIALOG(dialog));
704 }
705
706
707 gchar *
708 modest_images_cache_get_id (const gchar *account, const gchar *uri)
709 {
710         GnomeVFSURI *vfs_uri;
711         gchar *result;
712  
713         vfs_uri = gnome_vfs_uri_new (uri);
714         if (vfs_uri == NULL)
715                 return NULL;
716  
717         result = g_strdup_printf ("%s__%x", account, gnome_vfs_uri_hash (vfs_uri));
718         gnome_vfs_uri_unref (vfs_uri);
719  
720         return result;
721 }
722
723 gchar *
724 modest_utils_get_account_name_from_recipient (const gchar *from_header, gchar **mailbox)
725 {
726         gchar *account_name = NULL;
727         ModestAccountMgr *mgr = NULL;
728         GSList *accounts = NULL, *node = NULL;
729
730         if (mailbox)
731                 *mailbox = NULL;
732         g_return_val_if_fail (from_header, NULL);
733
734         mgr = modest_runtime_get_account_mgr ();
735         accounts = modest_account_mgr_account_names (mgr, TRUE);
736                 
737         for (node = accounts; node != NULL; node = g_slist_next (node)) {
738                 gchar *from;
739                 gchar *transport_account;
740
741                 if (!strcmp (from_header, node->data)) {
742                         account_name = g_strdup (node->data);
743                         break;
744                 }
745
746                 transport_account = modest_account_mgr_get_server_account_name (modest_runtime_get_account_mgr (),
747                                                                                 (const gchar *) node->data,
748                                                                                 TNY_ACCOUNT_TYPE_TRANSPORT);
749                 if (transport_account) {
750                         gchar *proto;
751                         proto = modest_account_mgr_get_string (mgr, transport_account, MODEST_ACCOUNT_PROTO, TRUE);
752
753                         if (proto != NULL) {
754                                 ModestProtocol *protocol = 
755                                         modest_protocol_registry_get_protocol_by_name (modest_runtime_get_protocol_registry (),
756                                                                                        MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS,
757                                                                                        proto);
758                                 if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
759                                         ModestPairList *pair_list;
760                                         ModestPair *pair;
761                                         gchar *from_header_email =
762                                                 modest_text_utils_get_email_address (from_header);
763                                         pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
764                                                                                            node->data);
765                                         
766                                         pair = modest_pair_list_find_by_first_as_string (pair_list, from_header_email);
767                                         if (pair != NULL) {
768                                                 account_name = g_strdup (node->data);
769                                                 if (mailbox)
770                                                         *mailbox = g_strdup (from_header_email);
771                                         }
772                                         
773                                         modest_pair_list_free (pair_list);
774                                         
775                                 }
776                                 g_free (proto);
777                         }
778                         g_free (transport_account);
779                 }
780                 if (mailbox && *mailbox)
781                         break;
782
783                 from = 
784                         modest_account_mgr_get_from_string (mgr, node->data, NULL);
785                         
786                 if (from) {
787                         gchar *from_email = 
788                                 modest_text_utils_get_email_address (from);
789                         gchar *from_header_email =
790                                 modest_text_utils_get_email_address (from_header);
791                                 
792                         if (from_email && from_header_email) {
793                                 if (!modest_text_utils_utf8_strcmp (from_header_email, from_email, TRUE)) {
794                                         account_name = g_strdup (node->data);
795                                         g_free (from);
796                                         g_free (from_email);
797                                         break;
798                                 }
799                         }
800                         g_free (from_email);
801                         g_free (from_header_email);
802                         g_free (from);
803                 }
804
805                         
806         }
807         g_slist_foreach (accounts, (GFunc) g_free, NULL);
808         g_slist_free (accounts);
809
810         return account_name;
811 }
812
813 void 
814 modest_utils_on_entry_invalid_character (ModestValidatingEntry *self, 
815                                          const gchar* character,
816                                          gpointer user_data)
817 {
818         gchar *message = NULL;
819         const gchar *show_char = NULL;
820
821         if (character)
822                 show_char = character;
823         else {
824                 show_char = "' '";
825         }
826         
827         message = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), show_char);
828         modest_platform_information_banner (GTK_WIDGET (self), NULL, message);
829         g_free (message);
830 }
831
832 FILE*
833 modest_utils_open_mcc_mapping_file (gboolean from_lc_messages, gboolean *translated)
834 {
835         FILE* result = NULL;
836         const gchar* path;
837         const gchar *env_list;
838         gchar **parts, **node;
839
840         if (from_lc_messages) {
841                 env_list = setlocale (LC_MESSAGES, NULL);
842         } else {
843                 env_list = getenv ("LANG");
844         }
845         parts = g_strsplit (env_list, ":", 0);
846         gchar *path1 = NULL;
847         const gchar* path2 = MODEST_MCC_MAPPING;
848
849         if (translated)
850                 *translated = TRUE;
851
852         path = NULL;
853         for (node = parts; path == NULL && node != NULL && *node != NULL && **node != '\0'; node++) {
854                 path1 = g_strdup_printf ("%s.%s", MODEST_OPERATOR_WIZARD_MCC_MAPPING, *node);
855                 if (access (path1, R_OK) == 0) {
856                         path = path1;
857                         break;
858                 } else {
859                         g_free (path1);
860                         path1 = NULL;
861                 }
862         }
863         g_strfreev (parts);
864
865         if (path == NULL) {
866                 if (access (MODEST_OPERATOR_WIZARD_MCC_MAPPING, R_OK) == 0) {
867                         path = MODEST_OPERATOR_WIZARD_MCC_MAPPING;
868                         if (translated)
869                                 *translated = FALSE;
870                 } else if (access (path2, R_OK) == 0) {
871                         path = path2;
872                 } else {
873                         g_warning ("%s: neither '%s' nor '%s' is a readable mapping file",
874                                    __FUNCTION__, path1, path2);
875                         goto end;
876                 }
877         }
878
879         result = fopen (path, "r");
880         if (!result) {
881                 g_warning ("%s: error opening mapping file '%s': %s",
882                            __FUNCTION__, path, strerror(errno));
883                 goto end;
884         }
885  end:
886         g_free (path1);
887         return result;
888 }
889
890 /* cluster mcc's, based on the list
891  * http://en.wikipedia.org/wiki/Mobile_country_code
892  */
893 static int
894 effective_mcc (gint mcc)
895 {
896         switch (mcc) {
897         case 405: return 404; /* india */
898         case 441: return 440; /* japan */       
899         case 235: return 234; /* united kingdom */
900         case 311:
901         case 312:
902         case 313:
903         case 314:
904         case 315:
905         case 316: return 310; /* united states */
906         default:  return mcc;
907         }
908 }
909
910 /* each line is of the form:
911    xxx    logical_id
912
913   NOTE: this function is NOT re-entrant, the out-param country
914   are static strings that should NOT be freed. and will change when
915   calling this function again
916
917   also note, this function will return the "effective mcc", which
918   is the normalized mcc for a country - ie. even if the there
919   are multiple entries for the United States with various mccs,
920   this function will always return 310, even if the real mcc parsed
921   would be 314. see the 'effective_mcc' function above.
922 */
923 static int
924 parse_mcc_mapping_line (const char* line,  char** country)
925 {
926         char mcc[4];  /* the mcc code, always 3 bytes*/
927         gchar *iter, *tab, *final;
928
929         if (!line) {
930                 *country = NULL;
931                 return 0;
932         }
933
934         /* Go to the first tab (Country separator) */
935         tab = g_utf8_strrchr (line, -1, '\t');
936         if (!tab)
937                 return 0;
938
939         *country = g_utf8_find_next_char (tab, NULL);
940
941         /* Replace by end of string. We need to use strlen, because
942            g_utf8_strrchr expects bytes and not UTF8 characters  */
943         final = g_utf8_strrchr (tab, strlen (tab) + 1, '\n');
944         if (G_LIKELY (final))
945                 *final = '\0';
946         else
947                 tab[strlen(tab) - 1] = '\0';
948
949         /* Get MCC code */
950         mcc[0] = g_utf8_get_char (line);
951         iter = g_utf8_find_next_char (line, NULL);
952         mcc[1] = g_utf8_get_char (iter);
953         iter = g_utf8_find_next_char (iter, NULL);
954         mcc[2] = g_utf8_get_char (iter);
955         mcc[3] = '\0';
956
957         return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10));
958 }
959
960 #define MCC_FILE_MAX_LINE_LEN 128 /* max length of a line in MCC file */
961
962 /** Note that the mcc_mapping file is installed 
963  * by the operator-wizard-settings package.
964  */
965 GtkTreeModel *
966 modest_utils_create_country_model (void)
967 {
968         GtkTreeModel *model;
969
970         model = GTK_TREE_MODEL (gtk_list_store_new (2,  G_TYPE_STRING, G_TYPE_INT));
971
972         return model;
973 }
974
975 void
976 modest_utils_fill_country_model (GtkTreeModel *model, gint *locale_mcc)
977 {
978         gboolean translated;
979         char line[MCC_FILE_MAX_LINE_LEN];
980         guint previous_mcc = 0;
981         gchar *territory;
982         GHashTable *country_hash;
983         FILE *file;
984
985         /* First we need to know our current region */
986         file = modest_utils_open_mcc_mapping_file (FALSE, &translated);
987         if (!file) {
988                 g_warning ("Could not open mcc_mapping file");
989                 return;
990         }
991
992         /* Get the territory specified for the current locale */
993         territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME);
994
995         while (fgets (line, MCC_FILE_MAX_LINE_LEN, file) != NULL) {
996                 int mcc;
997                 char *country = NULL;
998
999                 mcc = parse_mcc_mapping_line (line, &country);
1000                 if (!country || mcc == 0) {
1001                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
1002                         continue;
1003                 }
1004
1005                 if (mcc == previous_mcc) {
1006                         /* g_warning ("already seen: %s", line); */
1007                         continue;
1008                 }
1009                 previous_mcc = mcc;
1010
1011                 if (!(*locale_mcc)) {
1012                         if (translated) {
1013                                 if (!g_utf8_collate (country, territory))
1014                                         *locale_mcc = mcc;
1015                         } else {
1016                                 gchar *translation = dgettext ("osso-countries", country);
1017                                 if (!g_utf8_collate (translation, territory))
1018                                         *locale_mcc = mcc;
1019                         }
1020                 }
1021         }
1022         fclose (file);
1023
1024         /* Now we fill the model */
1025         file = modest_utils_open_mcc_mapping_file (TRUE, &translated);
1026         if (!file) {
1027                 g_warning ("Could not open mcc_mapping file");
1028                 return;
1029         }
1030
1031         country_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1032         while (fgets (line, MCC_FILE_MAX_LINE_LEN, file) != NULL) {
1033
1034                 int mcc;
1035                 char *country = NULL;
1036                 GtkTreeIter iter;
1037                 const gchar *name_translated;
1038
1039                 mcc = parse_mcc_mapping_line (line, &country);
1040                 if (!country || mcc == 0) {
1041                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
1042                         continue;
1043                 }
1044
1045                 if (mcc == previous_mcc ||
1046                     g_hash_table_lookup (country_hash, country)) {
1047                         g_debug ("already seen: '%s' %d", country, mcc);
1048                         continue;
1049                 }
1050                 previous_mcc = mcc;
1051
1052                 g_hash_table_insert (country_hash, g_strdup (country), GINT_TO_POINTER (mcc));
1053
1054                 name_translated = dgettext ("osso-countries", country);
1055
1056                 /* Add the row to the model: */
1057                 gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1058                 gtk_list_store_set(GTK_LIST_STORE (model), &iter, 
1059                                    MODEST_UTILS_COUNTRY_MODEL_COLUMN_MCC, mcc, 
1060                                    MODEST_UTILS_COUNTRY_MODEL_COLUMN_NAME, name_translated, 
1061                                    -1);
1062         }
1063
1064
1065         g_hash_table_unref (country_hash);
1066         fclose (file);
1067
1068         /* Fallback to Finland */
1069         if (!(*locale_mcc))
1070                 *locale_mcc = 244;
1071
1072         /* Sort the items: */
1073         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 
1074                                               MODEST_UTILS_COUNTRY_MODEL_COLUMN_NAME, GTK_SORT_ASCENDING);
1075 }
1076
1077 GList *
1078 modest_utils_create_notification_list_from_header_list (TnyList *header_list)
1079 {
1080         GList *new_headers_list;
1081         TnyIterator *iter;
1082
1083         g_return_val_if_fail (TNY_IS_LIST (header_list), NULL);
1084         g_return_val_if_fail (tny_list_get_length (header_list) > 0, NULL);
1085
1086         new_headers_list = NULL;
1087         iter = tny_list_create_iterator (header_list);
1088         while (!tny_iterator_is_done (iter)) {
1089                 ModestMsgNotificationData *data;
1090                 TnyHeader *header;
1091                 TnyFolder *folder;
1092
1093                 header = (TnyHeader *) tny_iterator_get_current (iter);
1094                 if (header) {
1095                         folder = tny_header_get_folder (header);
1096
1097                         if (folder) {
1098                                 gchar *uri, *uid;
1099
1100                                 uid = tny_header_dup_uid (header);
1101                                 uri = g_strdup_printf ("%s/%s",
1102                                                        tny_folder_get_url_string (folder),
1103                                                        uid);
1104                                 g_free (uid);
1105
1106                                 /* Create data & add to list */
1107                                 data = g_slice_new0 (ModestMsgNotificationData);
1108                                 data->subject = tny_header_dup_subject (header);
1109                                 data->from = tny_header_dup_from (header);
1110                                 data->uri = uri;
1111
1112                                 new_headers_list = g_list_append (new_headers_list, data);
1113
1114                                 g_object_unref (folder);
1115                         }
1116                         g_object_unref (header);
1117                 }
1118                 tny_iterator_next (iter);
1119         }
1120         g_object_unref (iter);
1121
1122         return new_headers_list;
1123 }
1124
1125 static void
1126 free_notification_data (gpointer data,
1127                         gpointer user_data)
1128 {
1129         ModestMsgNotificationData *notification_data  = (ModestMsgNotificationData *) data;
1130
1131         g_free (notification_data->from);
1132         g_free (notification_data->subject);
1133         g_free (notification_data->uri);
1134
1135         g_slice_free (ModestMsgNotificationData, notification_data);
1136 }
1137
1138 void
1139 modest_utils_free_notification_list (GList *notification_list)
1140 {
1141         g_return_if_fail (g_list_length (notification_list) > 0);
1142
1143         g_list_foreach (notification_list, free_notification_data, NULL);
1144         g_list_free (notification_list);
1145 }
1146
1147 void
1148 modest_utils_flush_send_queue (const gchar *account_id)
1149 {
1150         TnyTransportAccount *account;
1151
1152         /* Get the transport account */
1153         account = (TnyTransportAccount *)
1154                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1155                                                              account_id,
1156                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1157         if (account) {
1158                 ModestMailOperation *wakeup_op;
1159                 ModestTnySendQueue *send_queue = modest_runtime_get_send_queue (account, TRUE);
1160
1161                 /* Flush it! */
1162                 wakeup_op = modest_mail_operation_new (NULL);
1163                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1164                                                  wakeup_op);
1165                 modest_mail_operation_queue_wakeup (wakeup_op, send_queue);
1166
1167                 g_object_unref (account);
1168         }
1169 }