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