1 /* Copyright (c) 2006, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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.
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.
31 #include <glib/gi18n.h>
33 #include <tny-account.h>
34 #include <tny-account-store.h>
35 #include <tny-store-account.h>
36 #include <tny-transport-account.h>
37 #include <tny-device.h>
38 #include <tny-simple-list.h>
39 #include <tny-account-store.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 <modest-marshal.h>
44 #include <modest-protocol-info.h>
45 #include <modest-local-folder-info.h>
46 #include <modest-tny-account.h>
47 #include <modest-account-mgr.h>
48 #include <modest-account-mgr-helpers.h>
50 #include "modest-tny-account-store.h"
51 #include "modest-tny-platform-factory.h"
52 #include <tny-gtk-lockable.h>
53 #include <camel/camel.h>
55 /* 'private'/'protected' functions */
56 static void modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass);
57 //static void modest_tny_account_store_init (ModestTnyAccountStore *obj);
58 static void modest_tny_account_store_finalize (GObject *obj);
60 /* implementations for tny-account-store-iface */
61 static void modest_tny_account_store_instance_init (ModestTnyAccountStore *obj);
62 static void modest_tny_account_store_init (gpointer g, gpointer iface_data);
67 ACCOUNT_UPDATE_SIGNAL,
71 typedef struct _ModestTnyAccountStorePrivate ModestTnyAccountStorePrivate;
72 struct _ModestTnyAccountStorePrivate {
74 GHashTable *password_hash;
76 ModestAccountMgr *account_mgr;
77 TnySessionCamel *session;
79 /* we cache them here */
80 GSList *store_accounts;
81 GSList *transport_accounts;
84 #define MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
85 MODEST_TYPE_TNY_ACCOUNT_STORE, \
86 ModestTnyAccountStorePrivate))
88 static void on_password_requested (ModestTnyAccountStore *account_store,
89 const gchar* account_name,
95 static GObjectClass *parent_class = NULL;
97 static guint signals[LAST_SIGNAL] = {0};
100 modest_tny_account_store_get_type (void)
102 static GType my_type = 0;
105 static const GTypeInfo my_info = {
106 sizeof(ModestTnyAccountStoreClass),
107 NULL, /* base init */
108 NULL, /* base finalize */
109 (GClassInitFunc) modest_tny_account_store_class_init,
110 NULL, /* class finalize */
111 NULL, /* class data */
112 sizeof(ModestTnyAccountStore),
114 (GInstanceInitFunc) modest_tny_account_store_instance_init,
118 static const GInterfaceInfo iface_info = {
119 (GInterfaceInitFunc) modest_tny_account_store_init,
120 NULL, /* interface_finalize */
121 NULL /* interface_data */
124 my_type = g_type_register_static (G_TYPE_OBJECT,
125 "ModestTnyAccountStore",
127 g_type_add_interface_static (my_type, TNY_TYPE_ACCOUNT_STORE,
134 modest_tny_account_store_class_init (ModestTnyAccountStoreClass *klass)
136 GObjectClass *gobject_class;
137 gobject_class = (GObjectClass*) klass;
139 parent_class = g_type_class_peek_parent (klass);
140 gobject_class->finalize = modest_tny_account_store_finalize;
142 g_type_class_add_private (gobject_class,
143 sizeof(ModestTnyAccountStorePrivate));
145 signals[ACCOUNT_UPDATE_SIGNAL] =
146 g_signal_new ("account_update",
147 G_TYPE_FROM_CLASS (gobject_class),
149 G_STRUCT_OFFSET(ModestTnyAccountStoreClass, account_update),
151 g_cclosure_marshal_VOID__STRING,
152 G_TYPE_NONE, 1, G_TYPE_STRING);
157 modest_tny_account_store_instance_init (ModestTnyAccountStore *obj)
159 ModestTnyAccountStorePrivate *priv =
160 MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
162 priv->cache_dir = NULL;
163 priv->account_mgr = NULL;
165 priv->session = NULL;
167 priv->password_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
172 account_list_free (GSList *accounts)
174 GSList *cursor = accounts;
176 g_object_unref (G_OBJECT(cursor->data));
177 cursor = cursor->next;
179 g_slist_free (accounts);
184 on_account_removed (ModestAccountMgr *acc_mgr, const gchar *account, gboolean server_account,
187 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
188 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
190 /* FIXME: make this more finegrained; changes do not really affect _all_
191 * accounts, and some do not affect tny accounts at all (such as 'last_update')
193 account_list_free (priv->store_accounts);
194 priv->store_accounts = NULL;
196 account_list_free (priv->transport_accounts);
197 priv->transport_accounts = NULL;
199 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_UPDATE_SIGNAL], 0,
205 on_account_changed (ModestAccountMgr *acc_mgr, const gchar *account, gboolean server_account,
206 const gchar *key, gpointer user_data)
208 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(user_data);
209 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
211 /* FIXME: make this more finegrained; changes do not really affect _all_
212 * accounts, and some do not affect tny accounts at all (such as 'last_update')
214 account_list_free (priv->store_accounts);
215 priv->store_accounts = NULL;
217 account_list_free (priv->transport_accounts);
218 priv->transport_accounts = NULL;
221 g_signal_emit (G_OBJECT(self), signals[ACCOUNT_UPDATE_SIGNAL], 0,
226 static ModestTnyAccountStore*
227 get_account_store_for_account (TnyAccount *account)
229 return MODEST_TNY_ACCOUNT_STORE(g_object_get_data (G_OBJECT(account),
235 on_password_requested (ModestTnyAccountStore *account_store,
236 const gchar* account_name,
242 GtkWidget *dialog, *entry, *remember_pass_check;
244 dialog = gtk_dialog_new_with_buttons (_("Password requested"),
253 txt = g_strdup_printf (_("Please enter your password for %s"), account_name);
254 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), gtk_label_new(txt),
258 entry = gtk_entry_new_with_max_length (40);
259 gtk_entry_set_visibility (GTK_ENTRY(entry), FALSE);
260 gtk_entry_set_invisible_char (GTK_ENTRY(entry), 0x2022); /* bullet unichar */
262 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), entry,
265 remember_pass_check = gtk_check_button_new_with_label (_("Remember password"));
266 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog)->vbox), remember_pass_check,
269 gtk_widget_show_all (GTK_WIDGET(GTK_DIALOG(dialog)->vbox));
271 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) {
272 *password = g_strdup (gtk_entry_get_text (GTK_ENTRY(entry)));
279 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (remember_pass_check)))
284 gtk_widget_destroy (dialog);
288 get_password (TnyAccount *account, const gchar *prompt, gboolean *cancel)
291 const TnyAccountStore *account_store;
292 ModestTnyAccountStore *self;
293 ModestTnyAccountStorePrivate *priv;
296 gboolean already_asked;
298 key = tny_account_get_id (account);
299 account_store = TNY_ACCOUNT_STORE(get_account_store_for_account (account));
301 self = MODEST_TNY_ACCOUNT_STORE (account_store);
302 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
304 /* is it in the hash? if it's already there, it must be wrong... */
305 pwd_ptr = (gpointer)&pwd; /* pwd_ptr so the compiler does not complained about
306 * type-punned ptrs...*/
307 already_asked = g_hash_table_lookup_extended (priv->password_hash,
310 (gpointer*)&pwd_ptr);
312 /* if the password is not already there, try ModestConf */
313 if (!already_asked) {
314 pwd = modest_account_mgr_get_string (priv->account_mgr,
315 key, MODEST_ACCOUNT_PASSWORD,
317 g_hash_table_insert (priv->password_hash, g_strdup (key), g_strdup (pwd));
320 /* if it was already asked, it must have been wrong, so ask again */
321 if (already_asked || !pwd || strlen(pwd) == 0) {
323 /* we don't have it yet. Get the password from the user */
324 const gchar* name = tny_account_get_name (account);
328 on_password_requested (self, name, &pwd, cancel, &remember);
332 modest_account_mgr_set_string (priv->account_mgr,
333 key, MODEST_ACCOUNT_PASSWORD,
336 /* We need to dup the string even knowing that
337 it's already a dup of the contents of an
338 entry, because it if it's wrong, then camel
340 g_hash_table_insert (priv->password_hash, g_strdup (key), g_strdup(pwd));
342 g_hash_table_remove (priv->password_hash, key);
354 forget_password (TnyAccount *account)
356 ModestTnyAccountStore *self;
357 ModestTnyAccountStorePrivate *priv;
358 const TnyAccountStore *account_store;
362 account_store = TNY_ACCOUNT_STORE(get_account_store_for_account (account));
363 self = MODEST_TNY_ACCOUNT_STORE (account_store);
364 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
365 key = tny_account_get_id (account);
367 /* Do not remove the key, this will allow us to detect that we
368 have already asked for it at least once */
369 pwd = g_hash_table_lookup (priv->password_hash, key);
371 memset (pwd, 0, strlen (pwd));
372 g_hash_table_insert (priv->password_hash, g_strdup (key), NULL);
375 /* Remove from configuration system */
376 modest_account_mgr_unset (priv->account_mgr,
377 key, MODEST_ACCOUNT_PASSWORD,
383 modest_tny_account_store_finalize (GObject *obj)
385 ModestTnyAccountStore *self = MODEST_TNY_ACCOUNT_STORE(obj);
386 ModestTnyAccountStorePrivate *priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
388 g_free (priv->cache_dir);
389 priv->cache_dir = NULL;
392 g_object_unref (priv->device);
396 if (priv->password_hash) {
397 g_hash_table_destroy (priv->password_hash);
398 priv->password_hash = NULL;
401 if (priv->account_mgr) {
402 g_object_unref (priv->account_mgr);
403 priv->account_mgr = NULL;
407 camel_object_unref (CAMEL_OBJECT(priv->session));
408 priv->session = NULL;
411 /* this includes the local folder */
412 account_list_free (priv->store_accounts);
413 priv->store_accounts = NULL;
415 account_list_free (priv->transport_accounts);
416 priv->transport_accounts = NULL;
418 G_OBJECT_CLASS(parent_class)->finalize (obj);
422 ModestTnyAccountStore*
423 modest_tny_account_store_new (ModestAccountMgr *account_mgr) {
426 ModestTnyAccountStorePrivate *priv;
429 g_return_val_if_fail (account_mgr, NULL);
431 obj = G_OBJECT(g_object_new(MODEST_TYPE_TNY_ACCOUNT_STORE, NULL));
432 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(obj);
434 priv->account_mgr = account_mgr;
435 g_object_ref (G_OBJECT(priv->account_mgr));
437 priv->session = tny_session_camel_new (TNY_ACCOUNT_STORE(obj));
439 tny_session_camel_set_ui_locker (priv->session, tny_gtk_lockable_new ());
440 /* FIXME: unref this in the end? */
442 /* force a cache fill... ugly */
443 list = TNY_LIST(tny_simple_list_new());
444 tny_account_store_get_accounts (TNY_ACCOUNT_STORE(obj), list,
445 TNY_ACCOUNT_STORE_BOTH);
446 g_object_unref(list);
448 /* Connect signals */
449 g_signal_connect (G_OBJECT(account_mgr), "account_changed",
450 G_CALLBACK (on_account_changed), obj);
451 g_signal_connect (G_OBJECT(account_mgr), "account_removed",
452 G_CALLBACK (on_account_removed), obj);
454 return MODEST_TNY_ACCOUNT_STORE(obj);
459 get_cached_accounts (TnyAccountStore *self, TnyList *list, TnyAccountType type)
461 ModestTnyAccountStorePrivate *priv;
462 GSList *accounts, *cursor;
464 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
465 accounts = (type == TNY_ACCOUNT_TYPE_STORE ? priv->store_accounts : priv->transport_accounts);
469 tny_list_prepend (list, G_OBJECT(cursor->data));
470 cursor = cursor->next;
474 /* this function fills the TnyList, and also returns a GSList of the accounts,
475 * for caching purposes
478 get_accounts (TnyAccountStore *self, TnyList *list, TnyAccountType type)
480 ModestTnyAccountStorePrivate *priv;
481 GSList *account_names, *cursor;
482 GSList *accounts = NULL;
484 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
486 account_names = modest_account_mgr_account_names (priv->account_mgr, NULL);
488 for (cursor = account_names; cursor; cursor = cursor->next) {
490 gchar *account_name = (gchar*)cursor->data;
492 /* only return enabled accounts */
493 if (modest_account_mgr_get_enabled(priv->account_mgr, account_name)) {
494 TnyAccount *tny_account =
495 modest_tny_account_new_from_account (priv->account_mgr, account_name,
496 type, priv->session, get_password,
498 if (tny_account) { /* something went wrong */
499 g_object_set_data (G_OBJECT(tny_account), "account_store", (gpointer)self);
500 tny_list_prepend (list, G_OBJECT(tny_account));
501 accounts = g_slist_append (accounts, tny_account); /* cache it */
503 g_printerr ("modest: failed to create account for %s\n", account_name);
505 g_free (account_name);
507 g_slist_free (account_names);
509 /* also, add the local folder pseudo-account */
510 if (type == TNY_ACCOUNT_TYPE_STORE) {
511 TnyAccount *tny_account =
512 modest_tny_account_new_for_local_folders (priv->account_mgr, priv->session);
513 tny_list_prepend (list, G_OBJECT(tny_account));
514 accounts = g_slist_append (accounts, tny_account); /* cache it */
522 modest_tny_account_store_get_accounts (TnyAccountStore *self, TnyList *list,
523 TnyGetAccountsRequestType request_type)
525 ModestTnyAccountStorePrivate *priv;
527 g_return_if_fail (self);
528 g_return_if_fail (TNY_IS_LIST(list));
530 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
532 if (request_type == TNY_ACCOUNT_STORE_BOTH) {
533 modest_tny_account_store_get_accounts (self, list, TNY_ACCOUNT_STORE_STORE_ACCOUNTS);
534 modest_tny_account_store_get_accounts (self, list, TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS);
538 if (request_type == TNY_ACCOUNT_STORE_STORE_ACCOUNTS) {
540 if (!priv->store_accounts)
541 priv->store_accounts = get_accounts (self, list, TNY_ACCOUNT_TYPE_STORE);
543 get_cached_accounts (self, list, TNY_ACCOUNT_TYPE_STORE);
545 } else if (request_type == TNY_ACCOUNT_STORE_TRANSPORT_ACCOUNTS) {
547 if (!priv->transport_accounts)
548 priv->transport_accounts = get_accounts (self, list, TNY_ACCOUNT_TYPE_TRANSPORT);
550 get_cached_accounts (self, list, TNY_ACCOUNT_TYPE_TRANSPORT);
552 g_return_if_reached (); /* incorrect req type */
557 modest_tny_account_store_get_cache_dir (TnyAccountStore *self)
559 ModestTnyAccountStorePrivate *priv;
560 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
562 if (!priv->cache_dir)
563 priv->cache_dir = g_build_filename (g_get_home_dir(),
567 return priv->cache_dir;
572 * callers need to unref
575 modest_tny_account_store_get_device (TnyAccountStore *self)
577 ModestTnyAccountStorePrivate *priv;
579 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self);
582 priv->device = tny_platform_factory_new_device
583 (modest_tny_platform_factory_get_instance());
585 return g_object_ref (G_OBJECT(priv->device));
591 modest_tny_account_store_alert (TnyAccountStore *self, TnyAlertType type,
594 GtkMessageType gtktype;
595 gboolean retval = FALSE;
600 case TNY_ALERT_TYPE_INFO:
601 gtktype = GTK_MESSAGE_INFO;
603 case TNY_ALERT_TYPE_WARNING:
604 gtktype = GTK_MESSAGE_WARNING;
606 case TNY_ALERT_TYPE_ERROR:
608 gtktype = GTK_MESSAGE_ERROR;
612 dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL,
613 gtktype, GTK_BUTTONS_YES_NO, prompt);
615 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES)
618 gtk_widget_destroy (dialog);
626 modest_tny_account_store_add_store_account (TnyAccountStore *self,
627 TnyStoreAccount *account)
629 /* we should not need this...*/
630 g_printerr ("modest: add_store_account_func not implemented\n");
635 modest_tny_account_store_add_transport_account (TnyAccountStore *self,
636 TnyTransportAccount *account)
638 /* we should not need this...*/
639 g_printerr ("modest: add_transport_account_func not implemented\n");
645 modest_tny_account_store_init (gpointer g, gpointer iface_data)
647 TnyAccountStoreIface *klass;
649 g_return_if_fail (g);
651 klass = (TnyAccountStoreIface *)g;
653 klass->get_accounts_func =
654 modest_tny_account_store_get_accounts;
655 klass->add_transport_account_func =
656 modest_tny_account_store_add_transport_account;
657 klass->add_store_account_func =
658 modest_tny_account_store_add_store_account;
659 klass->get_cache_dir_func =
660 modest_tny_account_store_get_cache_dir;
661 klass->get_device_func =
662 modest_tny_account_store_get_device;
664 modest_tny_account_store_alert;
668 modest_tny_account_store_set_get_pass_func (ModestTnyAccountStore *self,
669 ModestTnyGetPassFunc func)
671 /* not implemented, we use signals */
672 g_printerr ("modest: set_get_pass_func not implemented\n");
676 tny_account_store_get_session (TnyAccountStore *self)
678 g_return_val_if_fail (self, NULL);
679 return MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE (self)->session;
684 modest_tny_account_store_get_tny_account_by_id (ModestTnyAccountStore *self, const gchar *id)
686 TnyAccount *account = NULL;
687 ModestTnyAccountStorePrivate *priv;
690 g_return_val_if_fail (self, NULL);
691 g_return_val_if_fail (id, NULL);
693 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
695 for (cursor = priv->store_accounts; cursor ; cursor = cursor->next) {
696 const gchar *acc_id = tny_account_get_id (TNY_ACCOUNT(cursor->data));
697 if (acc_id && strcmp (acc_id, id) == 0) {
698 account = TNY_ACCOUNT(cursor->data);
703 /* if we already found something, no need to search the transport accounts */
704 for (cursor = priv->transport_accounts; !account && cursor ; cursor = cursor->next) {
705 const gchar *acc_id = tny_account_get_id (TNY_ACCOUNT(cursor->data));
706 if (acc_id && strcmp (acc_id, id) == 0) {
707 account = TNY_ACCOUNT(cursor->data);
713 g_object_ref (G_OBJECT(account));
720 modest_tny_account_store_get_tny_account_by_account (ModestTnyAccountStore *self,
721 const gchar *account_name,
724 TnyAccount *account = NULL;
725 ModestAccountData *account_data;
726 const gchar *id = NULL;
727 ModestTnyAccountStorePrivate *priv;
729 g_return_val_if_fail (self, NULL);
730 g_return_val_if_fail (account_name, NULL);
731 g_return_val_if_fail (type == TNY_ACCOUNT_TYPE_STORE || type == TNY_ACCOUNT_TYPE_TRANSPORT,
734 priv = MODEST_TNY_ACCOUNT_STORE_GET_PRIVATE(self);
736 account_data = modest_account_mgr_get_account_data (priv->account_mgr, account_name);
738 g_printerr ("modest: cannot get account data for account '%s'\n", account_name);
742 if (type == TNY_ACCOUNT_TYPE_STORE && account_data->store_account)
743 id = account_data->store_account->account_name;
744 else if (account_data->transport_account)
745 id = account_data->transport_account->account_name;
748 account = modest_tny_account_store_get_tny_account_by_id (self, id);
750 g_printerr ("modest: could not get tny %s account for %s (id=%s)\n",
751 type == TNY_ACCOUNT_TYPE_STORE? "store" : "transport",
752 account_name, id ? id : "<none>");
754 modest_account_mgr_free_account_data (priv->account_mgr, account_data);