Removed MainWindow dependency from widgets
[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 #ifndef MODEST_TOOLKIT_HILDON2
557         if (MODEST_IS_MAIN_WINDOW (parent_window)) {
558                 header_view = MODEST_HEADER_VIEW(modest_main_window_get_child_widget (MODEST_MAIN_WINDOW(parent_window),
559                                                                                       MODEST_MAIN_WINDOW_WIDGET_TYPE_HEADER_VIEW));
560         }
561 #else
562         if (MODEST_IS_HEADER_WINDOW (parent_window)) {
563                 header_view = MODEST_HEADER_VIEW (modest_header_window_get_header_view (MODEST_HEADER_WINDOW (parent_window)));
564         }
565 #endif
566         if (!header_view)
567                 return;
568
569         /* Add sorting keys */
570         cols = modest_header_view_get_columns (header_view);
571         if (cols == NULL) 
572                 return;
573 #define SORT_ID_NUM 6
574         int sort_model_ids[SORT_ID_NUM];
575         int sort_ids[SORT_ID_NUM];
576
577         outgoing = (GPOINTER_TO_INT (g_object_get_data(G_OBJECT(cols->data), MODEST_HEADER_VIEW_COLUMN))==
578                     MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT);
579
580         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_sender_recipient"),
581                                                                     SORT_ID_NUM);
582         if (outgoing) {
583                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_TO_COLUMN;
584                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
585         } else {
586                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FROM_COLUMN;
587                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
588         }
589
590         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_date"),
591                                                             SORT_ID_NUM);
592         if (outgoing) {
593                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_SENT_TIME_T_COLUMN;
594                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_SENT_DATE;
595         } else {
596                 sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_DATE_RECEIVED_TIME_T_COLUMN;
597                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_RECEIVED_DATE;
598         }
599         default_key = sort_key;
600
601         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_subject"),
602                                                                     SORT_ID_NUM);
603         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_SUBJECT_COLUMN;
604         if (outgoing)
605                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_OUT;
606         else
607                 sort_ids[sort_key] = MODEST_HEADER_VIEW_COLUMN_COMPACT_HEADER_IN;
608
609         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_attachment"),
610                                                                     SORT_ID_NUM);
611         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
612         sort_ids[sort_key] = TNY_HEADER_FLAG_ATTACHMENTS;
613         attachments_sort_id = sort_key;
614
615         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_size"),
616                                                                     SORT_ID_NUM);
617         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_MESSAGE_SIZE_COLUMN;
618         sort_ids[sort_key] = 0;
619
620         sort_key = checked_modest_sort_criterium_view_add_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), _("mcen_li_sort_priority"),
621                                                                     SORT_ID_NUM);
622         sort_model_ids[sort_key] = TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN;
623         sort_ids[sort_key] = TNY_HEADER_FLAG_PRIORITY_MASK;
624         priority_sort_id = sort_key;
625         
626         sortable = GTK_TREE_SORTABLE (gtk_tree_view_get_model (GTK_TREE_VIEW (header_view)));
627         /* Launch dialogs */
628         if (!gtk_tree_sortable_get_sort_column_id (sortable,
629                                                    &current_sort_colid, &current_sort_type)) {
630                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), default_key);
631                 modest_sort_criterium_view_set_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog), GTK_SORT_DESCENDING);
632         } else {
633                 modest_sort_criterium_view_set_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog), current_sort_type);
634                 if (current_sort_colid == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
635                         gpointer flags_sort_type_pointer;
636                         flags_sort_type_pointer = g_object_get_data (G_OBJECT (cols->data), MODEST_HEADER_VIEW_FLAG_SORT);
637                         if (GPOINTER_TO_INT (flags_sort_type_pointer) == TNY_HEADER_FLAG_PRIORITY_MASK)
638                                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), priority_sort_id);
639                         else
640                                 modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), attachments_sort_id);
641                 } else {
642                         gint current_sort_keyid = 0;
643                         while (current_sort_keyid < SORT_ID_NUM) {
644                                 if (sort_model_ids[current_sort_keyid] == current_sort_colid)
645                                         break;
646                                 else 
647                                         current_sort_keyid++;
648                         }
649                         modest_sort_criterium_view_set_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog), current_sort_keyid);
650                 }
651         }
652
653         result = gtk_dialog_run (GTK_DIALOG (dialog));
654         if (result == GTK_RESPONSE_OK) {
655                 sort_key = modest_sort_criterium_view_get_sort_key (MODEST_SORT_CRITERIUM_VIEW (dialog));
656                 if (sort_key < 0 || sort_key > SORT_ID_NUM -1) {
657                         g_warning ("%s: out of range (%d)", __FUNCTION__, sort_key);
658                         sort_key = 0;
659                 }
660
661                 sort_type = modest_sort_criterium_view_get_sort_order (MODEST_SORT_CRITERIUM_VIEW (dialog));
662                 if (sort_model_ids[sort_key] == TNY_GTK_HEADER_LIST_MODEL_FLAGS_COLUMN) {
663                         g_object_set_data (G_OBJECT(cols->data), MODEST_HEADER_VIEW_FLAG_SORT,
664                                            GINT_TO_POINTER (sort_ids[sort_key]));
665                         /* This is a hack to make it resort rows always when flag fields are
666                          * selected. If we do not do this, changing sort field from priority to
667                          * attachments does not work */
668                         modest_header_view_sort_by_column_id (header_view, 0, sort_type);
669                 } else {
670                         gtk_tree_view_column_set_sort_column_id (GTK_TREE_VIEW_COLUMN (cols->data), 
671                                                                  sort_model_ids[sort_key]);
672                 }
673
674                 modest_header_view_sort_by_column_id (header_view, sort_model_ids[sort_key], sort_type);
675                 gtk_tree_sortable_sort_column_changed (sortable);
676         }
677
678         modest_widget_memory_save (modest_runtime_get_conf (),
679                                    G_OBJECT (header_view), MODEST_CONF_HEADER_VIEW_KEY);
680         
681         /* free */
682         g_list_free(cols);      
683 }
684
685 void
686 modest_utils_run_sort_dialog (GtkWindow *parent_window,
687                               ModestSortDialogType type)
688 {
689         GtkWidget *dialog = NULL;
690
691         /* Build dialog */
692         dialog = modest_platform_create_sort_dialog (parent_window);
693         if (dialog == NULL)
694                 return;
695         modest_window_mgr_set_modal (modest_runtime_get_window_mgr (),
696                                      GTK_WINDOW (dialog), parent_window);
697
698         /* Fill sort keys */
699         switch (type) {
700         case MODEST_SORT_HEADERS:
701                 launch_sort_headers_dialog (parent_window, 
702                                             GTK_DIALOG(dialog));
703                 break;
704         }
705         
706         /* Free */
707         on_destroy_dialog (GTK_DIALOG(dialog));
708 }
709
710
711 gchar *
712 modest_images_cache_get_id (const gchar *account, const gchar *uri)
713 {
714         GnomeVFSURI *vfs_uri;
715         gchar *result;
716  
717         vfs_uri = gnome_vfs_uri_new (uri);
718         if (vfs_uri == NULL)
719                 return NULL;
720  
721         result = g_strdup_printf ("%s__%x", account, gnome_vfs_uri_hash (vfs_uri));
722         gnome_vfs_uri_unref (vfs_uri);
723  
724         return result;
725 }
726
727 gchar *
728 modest_utils_get_account_name_from_recipient (const gchar *from_header, gchar **mailbox)
729 {
730         gchar *account_name = NULL;
731         ModestAccountMgr *mgr = NULL;
732         GSList *accounts = NULL, *node = NULL;
733
734         if (mailbox)
735                 *mailbox = NULL;
736         g_return_val_if_fail (from_header, NULL);
737
738         mgr = modest_runtime_get_account_mgr ();
739         accounts = modest_account_mgr_account_names (mgr, TRUE);
740                 
741         for (node = accounts; node != NULL; node = g_slist_next (node)) {
742                 gchar *from;
743                 gchar *transport_account;
744
745                 if (!strcmp (from_header, node->data)) {
746                         account_name = g_strdup (node->data);
747                         break;
748                 }
749
750                 transport_account = modest_account_mgr_get_server_account_name (modest_runtime_get_account_mgr (),
751                                                                                 (const gchar *) node->data,
752                                                                                 TNY_ACCOUNT_TYPE_TRANSPORT);
753                 if (transport_account) {
754                         gchar *proto;
755                         proto = modest_account_mgr_get_string (mgr, transport_account, MODEST_ACCOUNT_PROTO, TRUE);
756
757                         if (proto != NULL) {
758                                 ModestProtocol *protocol = 
759                                         modest_protocol_registry_get_protocol_by_name (modest_runtime_get_protocol_registry (),
760                                                                                        MODEST_PROTOCOL_REGISTRY_TRANSPORT_PROTOCOLS,
761                                                                                        proto);
762                                 if (protocol && MODEST_IS_ACCOUNT_PROTOCOL (protocol)) {
763                                         ModestPairList *pair_list;
764                                         ModestPair *pair;
765                                         gchar *from_header_email =
766                                                 modest_text_utils_get_email_address (from_header);
767                                         pair_list = modest_account_protocol_get_from_list (MODEST_ACCOUNT_PROTOCOL (protocol),
768                                                                                            node->data);
769                                         
770                                         pair = modest_pair_list_find_by_first_as_string (pair_list, from_header_email);
771                                         if (pair != NULL) {
772                                                 account_name = g_strdup (node->data);
773                                                 if (mailbox)
774                                                         *mailbox = g_strdup (from_header_email);
775                                         }
776                                         
777                                         modest_pair_list_free (pair_list);
778                                         
779                                 }
780                                 g_free (proto);
781                         }
782                         g_free (transport_account);
783                 }
784                 if (mailbox && *mailbox)
785                         break;
786
787                 from = 
788                         modest_account_mgr_get_from_string (mgr, node->data, NULL);
789                         
790                 if (from) {
791                         gchar *from_email = 
792                                 modest_text_utils_get_email_address (from);
793                         gchar *from_header_email =
794                                 modest_text_utils_get_email_address (from_header);
795                                 
796                         if (from_email && from_header_email) {
797                                 if (!modest_text_utils_utf8_strcmp (from_header_email, from_email, TRUE)) {
798                                         account_name = g_strdup (node->data);
799                                         g_free (from);
800                                         g_free (from_email);
801                                         break;
802                                 }
803                         }
804                         g_free (from_email);
805                         g_free (from_header_email);
806                         g_free (from);
807                 }
808
809                         
810         }
811         g_slist_foreach (accounts, (GFunc) g_free, NULL);
812         g_slist_free (accounts);
813
814         return account_name;
815 }
816
817 void 
818 modest_utils_on_entry_invalid_character (ModestValidatingEntry *self, 
819                                          const gchar* character,
820                                          gpointer user_data)
821 {
822         gchar *message = NULL;
823         const gchar *show_char = NULL;
824
825         if (character)
826                 show_char = character;
827         else {
828                 show_char = "' '";
829         }
830         
831         message = g_strdup_printf (_CS("ckdg_ib_illegal_characters_entered"), show_char);
832         modest_platform_information_banner (GTK_WIDGET (self), NULL, message);
833         g_free (message);
834 }
835
836 FILE*
837 modest_utils_open_mcc_mapping_file (void)
838 {
839         FILE* result = NULL;
840         const gchar* path;
841
842         if (access (MODEST_OPERATOR_WIZARD_MCC_MAPPING, R_OK) == 0) {
843                 path = MODEST_OPERATOR_WIZARD_MCC_MAPPING;
844         } else if (access (MODEST_MCC_MAPPING, R_OK) == 0) {
845                 path = MODEST_MCC_MAPPING;
846         } else {
847                 g_warning ("%s: neither '%s' nor '%s' is a readable mapping file",
848                            __FUNCTION__, MODEST_OPERATOR_WIZARD_MCC_MAPPING, MODEST_MCC_MAPPING);
849                 return NULL;
850         }
851
852         result = fopen (path, "r");
853         if (!result) {
854                 g_warning ("%s: error opening mapping file '%s': %s",
855                            __FUNCTION__, path, strerror(errno));
856         }
857
858         return result;
859 }
860
861 /* cluster mcc's, based on the list
862  * http://en.wikipedia.org/wiki/Mobile_country_code
863  */
864 static int
865 effective_mcc (gint mcc)
866 {
867         switch (mcc) {
868         case 405: return 404; /* india */
869         case 441: return 440; /* japan */
870         case 348: /* NOTE: see below */
871         case 235: return 234; /* united kingdom */
872         case 289: return 282; /* georgia */
873         case 549: /* NOTE: see below */
874         case 311:
875         case 312:
876         case 313:
877         case 314:
878         case 315:
879         case 316: return 310; /* united states */
880         default:  return mcc;
881         }
882         /* NOTE: 348 for UK and 549 for US are not correct, but we do
883            a workaround here as changing operator-wizard package is
884            more difficult */
885 }
886
887 /* each line is of the form:
888    xxx    logical_id
889
890   NOTE: this function is NOT re-entrant, the out-param country
891   are static strings that should NOT be freed. and will change when
892   calling this function again
893
894   also note, this function will return the "effective mcc", which
895   is the normalized mcc for a country - ie. even if the there
896   are multiple entries for the United States with various mccs,
897   this function will always return 310, even if the real mcc parsed
898   would be 314. see the 'effective_mcc' function above.
899 */
900 static int
901 parse_mcc_mapping_line (const char* line,  char** country)
902 {
903         char mcc[4];  /* the mcc code, always 3 bytes*/
904         gchar *iter, *tab, *final;
905
906         if (!line) {
907                 *country = NULL;
908                 return 0;
909         }
910
911         /* Go to the first tab (Country separator) */
912         tab = g_utf8_strrchr (line, -1, '\t');
913         if (!tab)
914                 return 0;
915
916         *country = g_utf8_find_next_char (tab, NULL);
917
918         /* Replace by end of string. We need to use strlen, because
919            g_utf8_strrchr expects bytes and not UTF8 characters. File
920            lines end with \r\n */
921         final = g_utf8_strrchr (tab, strlen (tab) + 1, '\r');
922         if (G_LIKELY (final))
923                 *final = '\0';
924         else
925                 tab[strlen(tab) - 1] = '\0';
926
927         /* Get MCC code */
928         mcc[0] = g_utf8_get_char (line);
929         iter = g_utf8_find_next_char (line, NULL);
930         mcc[1] = g_utf8_get_char (iter);
931         iter = g_utf8_find_next_char (iter, NULL);
932         mcc[2] = g_utf8_get_char (iter);
933         mcc[3] = '\0';
934
935         return effective_mcc ((int) strtol ((const char*)mcc, NULL, 10));
936 }
937
938 #define MCC_FILE_MAX_LINE_LEN 128 /* max length of a line in MCC file */
939
940 /** Note that the mcc_mapping file is installed 
941  * by the operator-wizard-settings package.
942  */
943 GtkTreeModel *
944 modest_utils_create_country_model (void)
945 {
946         GtkTreeModel *model;
947
948         model = GTK_TREE_MODEL (gtk_list_store_new (2,  G_TYPE_STRING, G_TYPE_INT));
949
950         return model;
951 }
952
953 void
954 modest_utils_fill_country_model (GtkTreeModel *model, gint *locale_mcc)
955 {
956         char line[MCC_FILE_MAX_LINE_LEN];
957         guint previous_mcc = 0;
958         gchar *territory;
959         GHashTable *country_hash;
960         FILE *file;
961
962         /* First we need to know our current region */
963         file = modest_utils_open_mcc_mapping_file ();
964         if (!file) {
965                 g_warning ("Could not open mcc_mapping file");
966                 return;
967         }
968
969         /* Get the territory specified for the current locale */
970         territory = nl_langinfo (_NL_ADDRESS_COUNTRY_NAME);
971
972         while (fgets (line, MCC_FILE_MAX_LINE_LEN, file) != NULL) {
973                 int mcc;
974                 char *country = NULL;
975
976                 mcc = parse_mcc_mapping_line (line, &country);
977                 if (!country || mcc == 0) {
978                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
979                         continue;
980                 }
981
982                 if (mcc == previous_mcc) {
983                         /* g_warning ("already seen: %s", line); */
984                         continue;
985                 }
986                 previous_mcc = mcc;
987
988                 if (!(*locale_mcc)) {
989                         gchar *translation = dgettext ("osso-countries", country);
990                         if (!g_utf8_collate (translation, territory))
991                                 *locale_mcc = mcc;
992                 }
993         }
994
995         /* Now we fill the model */
996         rewind (file);
997         country_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
998         while (fgets (line, MCC_FILE_MAX_LINE_LEN, file) != NULL) {
999
1000                 int mcc;
1001                 char *country = NULL;
1002                 GtkTreeIter iter;
1003                 const gchar *name_translated;
1004
1005                 mcc = parse_mcc_mapping_line (line, &country);
1006                 if (!country || mcc == 0) {
1007                         g_warning ("%s: error parsing line: '%s'", __FUNCTION__, line);
1008                         continue;
1009                 }
1010
1011                 if (mcc == previous_mcc ||
1012                     g_hash_table_lookup (country_hash, country)) {
1013                         g_debug ("already seen: '%s' %d", country, mcc);
1014                         continue;
1015                 }
1016                 previous_mcc = mcc;
1017
1018                 g_hash_table_insert (country_hash, g_strdup (country), GINT_TO_POINTER (mcc));
1019
1020                 name_translated = dgettext ("osso-countries", country);
1021
1022                 /* Add the row to the model if we have translation for it */
1023                 if (g_utf8_collate (country, name_translated)) {
1024                         gtk_list_store_append (GTK_LIST_STORE (model), &iter);
1025                         gtk_list_store_set(GTK_LIST_STORE (model), &iter,
1026                                            MODEST_UTILS_COUNTRY_MODEL_COLUMN_MCC, mcc,
1027                                            MODEST_UTILS_COUNTRY_MODEL_COLUMN_NAME, name_translated,
1028                                            -1);
1029                 } else {
1030                         g_debug ("%s no translation for %s", __FUNCTION__, country);
1031                 }
1032         }
1033
1034
1035         g_hash_table_unref (country_hash);
1036         fclose (file);
1037
1038         /* Fallback to Finland */
1039         if (!(*locale_mcc))
1040                 *locale_mcc = 244;
1041
1042         /* Sort the items: */
1043         gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (model), 
1044                                               MODEST_UTILS_COUNTRY_MODEL_COLUMN_NAME, GTK_SORT_ASCENDING);
1045 }
1046
1047 GList *
1048 modest_utils_create_notification_list_from_header_list (TnyList *header_list)
1049 {
1050         GList *new_headers_list;
1051         TnyIterator *iter;
1052
1053         g_return_val_if_fail (TNY_IS_LIST (header_list), NULL);
1054         g_return_val_if_fail (tny_list_get_length (header_list) > 0, NULL);
1055
1056         new_headers_list = NULL;
1057         iter = tny_list_create_iterator (header_list);
1058         while (!tny_iterator_is_done (iter)) {
1059                 ModestMsgNotificationData *data;
1060                 TnyHeader *header;
1061                 TnyFolder *folder;
1062
1063                 header = (TnyHeader *) tny_iterator_get_current (iter);
1064                 if (header) {
1065                         folder = tny_header_get_folder (header);
1066
1067                         if (folder) {
1068                                 gchar *uri, *uid;
1069
1070                                 uid = tny_header_dup_uid (header);
1071                                 uri = g_strdup_printf ("%s/%s",
1072                                                        tny_folder_get_url_string (folder),
1073                                                        uid);
1074                                 g_free (uid);
1075
1076                                 /* Create data & add to list */
1077                                 data = g_slice_new0 (ModestMsgNotificationData);
1078                                 data->subject = tny_header_dup_subject (header);
1079                                 data->from = tny_header_dup_from (header);
1080                                 data->uri = uri;
1081
1082                                 new_headers_list = g_list_append (new_headers_list, data);
1083
1084                                 g_object_unref (folder);
1085                         }
1086                         g_object_unref (header);
1087                 }
1088                 tny_iterator_next (iter);
1089         }
1090         g_object_unref (iter);
1091
1092         return new_headers_list;
1093 }
1094
1095 static void
1096 free_notification_data (gpointer data,
1097                         gpointer user_data)
1098 {
1099         ModestMsgNotificationData *notification_data  = (ModestMsgNotificationData *) data;
1100
1101         g_free (notification_data->from);
1102         g_free (notification_data->subject);
1103         g_free (notification_data->uri);
1104
1105         g_slice_free (ModestMsgNotificationData, notification_data);
1106 }
1107
1108 void
1109 modest_utils_free_notification_list (GList *notification_list)
1110 {
1111         g_return_if_fail (g_list_length (notification_list) > 0);
1112
1113         g_list_foreach (notification_list, free_notification_data, NULL);
1114         g_list_free (notification_list);
1115 }
1116
1117 void
1118 modest_utils_flush_send_queue (const gchar *account_id)
1119 {
1120         TnyTransportAccount *account;
1121
1122         /* Get the transport account */
1123         account = (TnyTransportAccount *)
1124                 modest_tny_account_store_get_server_account (modest_runtime_get_account_store (),
1125                                                              account_id,
1126                                                              TNY_ACCOUNT_TYPE_TRANSPORT);
1127         if (account) {
1128                 ModestMailOperation *wakeup_op;
1129                 ModestTnySendQueue *send_queue = modest_runtime_get_send_queue (account, TRUE);
1130
1131                 /* Flush it! */
1132                 wakeup_op = modest_mail_operation_new (NULL);
1133                 modest_mail_operation_queue_add (modest_runtime_get_mail_operation_queue (),
1134                                                  wakeup_op);
1135                 modest_mail_operation_queue_wakeup (wakeup_op, send_queue);
1136
1137                 g_object_unref (account);
1138         }
1139 }