Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / libedataserverui / e-passwords.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
3 /*
4  * e-passwords.c
5  *
6  * Copyright (C) 2001 Ximian, Inc.
7  */
8
9 /*
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of version 2 of the GNU Lesser General Public
12  * License as published by the Free Software Foundation.
13  * 
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Lesser General Public License for more details.
18  * 
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
22  * USA.
23  */
24
25 /*
26  * This looks a lot more complicated than it is, and than you'd think
27  * it would need to be.  There is however, method to the madness.
28  *
29  * The code most cope with being called from any thread at any time,
30  * recursively from the main thread, and then serialising every
31  * request so that sane and correct values are always returned, and
32  * duplicate requests are never made.
33  *
34  * To this end, every call is marshalled and queued and a dispatch
35  * method invoked until that request is satisfied.  If mainloop
36  * recursion occurs, then the sub-call will necessarily return out of
37  * order, but will not be processed out of order.
38  */
39
40 #ifdef HAVE_CONFIG_H
41 #include "config.h"
42 #endif
43
44 #include <string.h>
45 #include <libgnome/gnome-config.h>
46 #include <glib/gi18n-lib.h>
47 #include <gtk/gtkversion.h>
48 #include <gtk/gtkentry.h>
49 #include <gtk/gtkvbox.h>
50 #include <gtk/gtkcheckbutton.h>
51 #include <gtk/gtkmessagedialog.h>
52
53 #include "e-passwords.h"
54 #include "libedataserver/e-msgport.h"
55 #include "libedataserver/e-url.h"
56
57 #ifdef WITH_GNOME_KEYRING
58 #include <gnome-keyring.h>
59 #endif
60
61 #ifndef ENABLE_THREADS
62 #define ENABLE_THREADS (1)
63 #endif
64
65 #ifdef ENABLE_THREADS
66 #include <pthread.h>
67
68 static pthread_t main_thread;
69 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
70 #define LOCK() pthread_mutex_lock(&lock)
71 #define UNLOCK() pthread_mutex_unlock(&lock)
72 #else
73 #define LOCK()
74 #define UNLOCK()
75 #endif
76
77 #define d(x) x
78
79 struct _EPassMsg {
80         EMsg msg;
81
82         void (*dispatch)(struct _EPassMsg *);
83
84         /* input */
85         struct _GtkWindow *parent;      
86         const char *component;
87         const char *key;
88         const char *title;
89         const char *prompt;
90         const char *oldpass;
91         guint32 flags;
92
93         /* output */
94         gboolean *remember;
95         char *password;
96
97         /* work variables */
98         GtkWidget *entry;
99         GtkWidget *check;
100         guint ismain:1;
101         guint noreply:1;        /* supress replies; when calling
102                                  * dispatch functions from others */
103 };
104
105 typedef struct _EPassMsg EPassMsg;
106
107 static GHashTable *passwords = NULL;
108 static GtkDialog *password_dialog = NULL;
109 static EDList request_list = E_DLIST_INITIALISER(request_list);
110 static int idle_id;
111 static int ep_online_state = TRUE;
112
113 static char *decode_base64 (char *base64);
114 static int base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save);
115 static int base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save);
116
117 static gboolean
118 ep_idle_dispatch(void *data)
119 {
120         EPassMsg *msg;
121
122         /* As soon as a password window is up we stop; it will
123            re-invoke us when it has been closed down */
124         LOCK();
125         while (password_dialog == NULL && (msg = (EPassMsg *)e_dlist_remhead(&request_list))) {
126                 UNLOCK();
127
128                 msg->dispatch(msg);
129
130                 LOCK();
131         }
132
133         idle_id = 0;
134         UNLOCK();
135
136         return FALSE;
137 }
138
139 static EPassMsg *
140 ep_msg_new(void (*dispatch)(EPassMsg *))
141 {
142         EPassMsg *msg;
143
144         e_passwords_init();
145
146         msg = g_malloc0(sizeof(*msg));
147         msg->dispatch = dispatch;
148         msg->msg.reply_port = e_msgport_new();
149 #ifdef ENABLE_THREADS
150         msg->ismain = pthread_equal(pthread_self(), main_thread);
151 #else
152         msg->ismain = TRUE;
153 #endif
154         return msg;
155 }
156
157 static void
158 ep_msg_free(EPassMsg *msg)
159 {
160         e_msgport_destroy(msg->msg.reply_port);
161         g_free(msg->password);
162         g_free(msg);
163 }
164
165 static void
166 ep_msg_send(EPassMsg *msg)
167 {
168         int needidle = 0;
169
170         LOCK();
171         e_dlist_addtail(&request_list, (EDListNode *)&msg->msg);
172         if (!idle_id) {
173                 if (!msg->ismain)
174                         idle_id = g_idle_add(ep_idle_dispatch, NULL);
175                 else
176                         needidle = 1;
177         }
178         UNLOCK();
179
180         if (msg->ismain) {
181                 EPassMsg *m;
182
183                 if (needidle)
184                         ep_idle_dispatch(NULL);
185                 while ((m = (EPassMsg *)e_msgport_get(msg->msg.reply_port)) == NULL)
186                         g_main_context_iteration(NULL, TRUE);
187                 g_assert(m == msg);
188         } else {
189                 EMsg *reply_msg = e_msgport_wait(msg->msg.reply_port);
190                 g_assert(reply_msg == &msg->msg);
191         }
192 }
193
194 /* the functions that actually do the work */
195 #ifdef WITH_GNOME_KEYRING
196 static void
197 ep_clear_passwords_keyring(EPassMsg *msg)
198 {
199         GnomeKeyringAttributeList *attributes;
200         GnomeKeyringResult result;
201         GList *matches = NULL, *tmp;    
202         char *default_keyring = NULL;
203
204         result = gnome_keyring_get_default_keyring_sync (&default_keyring);
205         if (!default_keyring) {
206                 if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) {
207                                 if (!msg->noreply)
208                                         e_msgport_reply(&msg->msg);
209                                 return;
210                 }
211                 default_keyring = g_strdup ("default");                 
212         }
213
214         d(g_print("Get Default %d\n", result));
215         
216         /* Not called at all */
217         attributes = gnome_keyring_attribute_list_new ();
218         gnome_keyring_attribute_list_append_string (attributes, "application", "Evolution");
219
220         result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &matches);
221         d(g_print ("Find Items %d\n", result));
222                 
223         gnome_keyring_attribute_list_free (attributes);
224
225         if (result) {
226                 g_print ("Couldn't clear password");
227         } else {
228                 for (tmp = matches; tmp; tmp = tmp->next) {
229                         result = gnome_keyring_item_delete_sync (default_keyring, ((GnomeKeyringFound *) tmp->data)->item_id);
230                         d(g_print("Delete Items %d\n", result));
231                 }
232         }
233         
234         g_free (default_keyring);
235         if (!msg->noreply)
236                 e_msgport_reply(&msg->msg);
237 }
238 #endif
239 static void
240 ep_clear_passwords_file(EPassMsg *msg)
241 {
242         char *path;
243
244         path = g_strdup_printf ("/Evolution/Passwords-%s", msg->component);
245
246         gnome_config_private_clean_section (path);
247         gnome_config_private_sync_file ("/Evolution");
248
249         g_free (path);
250
251         if (!msg->noreply)
252                 e_msgport_reply(&msg->msg);
253 }
254
255 static gboolean
256 free_entry (gpointer key, gpointer value, gpointer user_data)
257 {
258         g_free (key);
259         memset (value, 0, strlen (value));
260         g_free (value);
261         return TRUE;
262 }
263
264 #ifdef WITH_GNOME_KEYRING
265 static void
266 ep_forget_passwords_keyring(EPassMsg *msg)
267 {
268         GnomeKeyringAttributeList *attributes;
269         GnomeKeyringResult result;
270         GList *matches = NULL, *tmp;    
271         char *default_keyring = NULL;
272
273         result = gnome_keyring_get_default_keyring_sync (&default_keyring);
274         if (!default_keyring) {
275                 if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK) {
276                                 if (!msg->noreply)
277                                         e_msgport_reply(&msg->msg);
278                                 return;
279                 }
280                 default_keyring = g_strdup ("default");                 
281         }       
282         d(g_print("Get Default %d\n", result));
283         
284         attributes = gnome_keyring_attribute_list_new ();
285         gnome_keyring_attribute_list_append_string (attributes, "application", "Evolution");
286
287         result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &matches);
288         d(g_print ("Find Items %d\n", result));
289                 
290         gnome_keyring_attribute_list_free (attributes);
291
292         if (result) {
293                 g_print ("Couldn't clear password");
294         } else {
295                 for (tmp = matches; tmp; tmp = tmp->next) {
296                         result = gnome_keyring_item_delete_sync (default_keyring, ((GnomeKeyringFound *) tmp->data)->item_id);
297                         d(g_print("Delete Items %d\n", result));
298                 }
299         }
300         
301         g_free (default_keyring);
302
303         /* free up the session passwords */
304         g_hash_table_foreach_remove (passwords, free_entry, NULL);
305
306         if (!msg->noreply)
307                 e_msgport_reply(&msg->msg);
308 }
309 #endif
310
311 static void
312 ep_forget_passwords_file(EPassMsg *msg)
313 {
314         void *it;
315         char *key;
316
317         it = gnome_config_private_init_iterator_sections("/Evolution");
318         while ( (it = gnome_config_iterator_next(it, &key, NULL)) ) {
319                 if (0 == strncmp(key, "Passwords-", 10)) {
320                         char *section = g_strdup_printf("/Evolution/%s", key);
321
322                         gnome_config_private_clean_section (section);
323                         g_free(section);
324                 }
325                 g_free(key);
326         }
327
328         gnome_config_private_sync_file ("/Evolution");
329
330         /* free up the session passwords */
331         g_hash_table_foreach_remove (passwords, free_entry, NULL);
332
333         if (!msg->noreply)
334                 e_msgport_reply(&msg->msg);
335 }
336
337 static char *
338 password_path (const char *component_name, const char *key)
339 {
340         char *keycopy, *path;
341         int i;
342         keycopy = g_strdup (key);
343
344         for (i = 0; i < strlen (keycopy); i ++)
345                 if (keycopy[i] == '/' || keycopy[i] =='=')
346                         keycopy[i] = '_';
347         
348         path = g_strdup_printf ("/Evolution/Passwords-%s/%s", component_name, keycopy);
349
350         g_free (keycopy);
351
352         return path;
353 }
354
355 #ifdef WITH_GNOME_KEYRING
356 static void
357 ep_remember_password_keyring(EPassMsg *msg)
358 {
359         gpointer okey, value;
360
361         if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) {
362                 /* add it to the on-disk cache of passwords */
363                 GnomeKeyringAttributeList *attributes;
364                 GnomeKeyringResult result;
365                 EUri *uri = e_uri_new (okey);
366                 guint32 item_id;
367
368                 if (!strcmp (uri->protocol, "ldap") && !uri->user) {
369                         /* LDAP doesnt use username in url. Let the url be the user key. So safe it */
370                         char *keycopy = g_strdup (msg->key);
371                         int i;
372                         
373                         for (i = 0; i < strlen (keycopy); i ++)
374                                 if (keycopy[i] == '/' || keycopy[i] =='=')
375                                         keycopy[i] = '_';               
376                         uri->user = keycopy;
377                 }
378                 
379                 attributes = gnome_keyring_attribute_list_new ();
380                 gnome_keyring_attribute_list_append_string (attributes, "user", uri->user);
381                 gnome_keyring_attribute_list_append_string (attributes, "server", uri->host);
382                 gnome_keyring_attribute_list_append_string (attributes, "application", "Evolution");
383                 
384                 result = gnome_keyring_item_create_sync (NULL, /* Use default keyring */
385                                                          GNOME_KEYRING_ITEM_NETWORK_PASSWORD, /* type */
386                                                          msg->key, /* name */
387                                                          attributes, /* attribute list */
388                                                          value, /* password */
389                                                          TRUE, /* Update if already exists */
390                                                          &item_id);
391         
392                 d(g_print("Remember %s: %d/%d\n", msg->key, result, item_id));
393
394                 gnome_keyring_attribute_list_free (attributes);
395
396                 /* now remove it from our session hash */
397                 g_hash_table_remove (passwords, msg->key);
398                 g_free (okey);
399                 g_free (value);
400                 
401                 e_uri_free (uri);
402         }
403
404         if (!msg->noreply)
405                 e_msgport_reply(&msg->msg);
406 }
407 #endif
408
409 static void
410 ep_remember_password_file(EPassMsg *msg)
411 {
412         gpointer okey, value;
413         char *path, *pass64;
414         int len, state, save;
415
416         if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) {
417                 /* add it to the on-disk cache of passwords */
418                 path = password_path (msg->component, okey);
419
420                 len = strlen (value);
421                 pass64 = g_malloc0 ((len + 2) * 4 / 3 + 1);
422                 state = save = 0;
423                 base64_encode_close (value, len, FALSE, (guchar *)pass64, &state, &save);
424
425                 gnome_config_private_set_string (path, pass64);
426                 g_free (path);
427                 g_free (pass64);
428
429                 /* now remove it from our session hash */
430                 g_hash_table_remove (passwords, msg->key);
431                 g_free (okey);
432                 g_free (value);
433
434                 gnome_config_private_sync_file ("/Evolution");
435         }
436
437         if (!msg->noreply)
438                 e_msgport_reply(&msg->msg);
439 }
440
441 #ifdef WITH_GNOME_KEYRING
442 static void
443 ep_forget_password_keyring (EPassMsg *msg)
444 {
445         GnomeKeyringAttributeList *attributes;
446         GnomeKeyringResult result;
447         GList *matches = NULL, *tmp;    
448         char *default_keyring = NULL;   
449         gpointer okey, value;
450         EUri *uri = e_uri_new (msg->key);
451
452         if (!strcmp (uri->protocol, "ldap") && !uri->user) {
453                 /* LDAP doesnt use username in url. Let the url be the user key. So safe it */
454                 char *keycopy = g_strdup (msg->key);
455                 int i;
456
457                 for (i = 0; i < strlen (keycopy); i ++)
458                         if (keycopy[i] == '/' || keycopy[i] =='=')
459                                 keycopy[i] = '_';               
460                 uri->user = keycopy;
461         }
462             
463         if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) {
464                 g_hash_table_remove (passwords, msg->key);
465                 memset (value, 0, strlen (value));
466                 g_free (okey);
467                 g_free (value);
468         }
469
470         if (!uri->host && !uri->user)
471                 /* No need to remove from keyring for pass phrases */
472                 goto exit;
473         
474         result = gnome_keyring_get_default_keyring_sync (&default_keyring);
475         if (!default_keyring) {
476                 if (gnome_keyring_create_sync ("default", NULL) != GNOME_KEYRING_RESULT_OK)
477                         goto exit;
478                 default_keyring = g_strdup ("default");                 
479         }
480
481         d(g_print("Get Default %d\n", result));
482         
483         attributes = gnome_keyring_attribute_list_new ();
484         gnome_keyring_attribute_list_append_string (attributes, "user", uri->user);
485         gnome_keyring_attribute_list_append_string (attributes, "server", uri->host);
486         gnome_keyring_attribute_list_append_string (attributes, "application", "Evolution");
487
488         result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &matches);
489         d(g_print ("Find Items %d\n", result));
490                 
491         gnome_keyring_attribute_list_free (attributes);
492
493         if (result) {
494                 g_print ("Couldn't clear password");
495         } else {
496                 for (tmp = matches; tmp; tmp = tmp->next) {
497                         GArray *pattr = ((GnomeKeyringFound *) tmp->data)->attributes;
498                         int i;
499                         GnomeKeyringAttribute *attr;
500                         gboolean accept = TRUE;
501                         guint present = 0;
502
503                         for (i =0; (i < pattr->len) && accept; i++)
504                         {
505                                 attr = &g_array_index (pattr, GnomeKeyringAttribute, i);
506                                 if (!strcmp(attr->name, "user")) {
507                                         present++;
508                                         if (strcmp (attr->value.string, uri->user))
509                                                 accept = FALSE;
510                                 } else if (!strcmp(attr->name, "server")) {
511                                         present++;
512                                         if (strcmp (attr->value.string, uri->host))
513                                                 accept = FALSE;                                         
514                                 }
515                         }
516                                 if (present == 2 && accept) {
517                                         result = gnome_keyring_item_delete_sync (default_keyring, ((GnomeKeyringFound *) tmp->data)->item_id);
518                                         d(g_print("Delete Items %s %s %d\n", uri->host, uri->user, result));                    
519                                 }
520                 }       
521
522         }
523         
524         g_free (default_keyring);
525
526 exit:
527         if (!msg->noreply)
528                 e_msgport_reply(&msg->msg);
529
530         e_uri_free(uri);
531 }
532 #endif
533
534 static void
535 ep_forget_password_file (EPassMsg *msg)
536 {
537         gpointer okey, value;
538         char *path;
539
540         if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) {
541                 g_hash_table_remove (passwords, msg->key);
542                 memset (value, 0, strlen (value));
543                 g_free (okey);
544                 g_free (value);
545         }
546
547         /* clear it in the on disk db */
548         path = password_path (msg->component, msg->key);
549         gnome_config_private_clean_key (path);
550         gnome_config_private_sync_file ("/Evolution");
551         g_free (path);
552         
553         if (!msg->noreply)
554                 e_msgport_reply(&msg->msg);
555 }
556
557
558 #ifdef WITH_GNOME_KEYRING
559 static void
560 ep_get_password_keyring (EPassMsg *msg)
561 {
562         char *passwd;
563         GnomeKeyringAttributeList *attributes;
564         GnomeKeyringResult result;
565         GList *matches = NULL, *tmp;    
566
567         passwd = g_hash_table_lookup (passwords, msg->key);
568         if (passwd) {
569                 msg->password = g_strdup(passwd);
570         } else {
571                 EUri *uri = e_uri_new (msg->key);
572                 
573                 if (!strcmp (uri->protocol, "ldap") && !uri->user) {
574                         /* LDAP doesnt use username in url. Let the url be the user key. So safe it */
575                         char *keycopy = g_strdup (msg->key);
576                         int i;
577
578                         for (i = 0; i < strlen (keycopy); i ++)
579                                 if (keycopy[i] == '/' || keycopy[i] =='=')
580                                         keycopy[i] = '_';               
581                         uri->user = keycopy;
582                 }
583                 
584                 if (uri->host &&  uri->user) {
585                         /* We dont store passphrases.*/
586
587                         attributes = gnome_keyring_attribute_list_new ();
588                         gnome_keyring_attribute_list_append_string (attributes, "user", uri->user);
589                         gnome_keyring_attribute_list_append_string (attributes, "server", uri->host);
590                         gnome_keyring_attribute_list_append_string (attributes, "application", "Evolution");
591                         printf("get %s %s\n", uri->user, msg->key);
592
593                         result = gnome_keyring_find_items_sync (GNOME_KEYRING_ITEM_NETWORK_PASSWORD, attributes, &matches);
594                         d(g_print ("Find Items %d\n", result));
595                         
596                         gnome_keyring_attribute_list_free (attributes);
597
598                         if (result) {
599                                 g_print ("Couldn't Get password %d\n", result);
600                         } else {
601                                 /* FIXME: What to do if this returns more than one? */
602                                 for (tmp = matches; tmp; tmp = tmp->next) {
603                                         GArray *pattr = ((GnomeKeyringFound *) tmp->data)->attributes;
604                                         int i;
605                                         GnomeKeyringAttribute *attr;
606                                         gboolean accept = TRUE;
607                                         guint present = 0;
608
609                                         for (i =0; (i < pattr->len) && accept; i++)
610                                         {
611                                                 attr = &g_array_index (pattr, GnomeKeyringAttribute, i);
612
613                                                 if (!strcmp(attr->name, "user") && attr->value.string) {
614                                                         present++;
615                                                         if (strcmp (attr->value.string, uri->user))
616                                                                 accept = FALSE;
617                                                 } else if (!strcmp(attr->name, "server") && attr->value.string) {
618                                                         present++;
619                                                         if (strcmp (attr->value.string, uri->host))
620                                                                 accept = FALSE;                                         
621                                                 }
622                                         }
623                                         if (present == 2 && accept) {
624                                                 msg->password = g_strdup (((GnomeKeyringFound *) tmp->data)->secret);
625                                                 break;
626                                         }
627                                 }       
628                         }
629                 }
630                 
631                 e_uri_free (uri);
632         }
633
634         if (!msg->noreply)
635                 e_msgport_reply(&msg->msg);
636 }       
637 #endif
638
639 static void
640 ep_get_password_file (EPassMsg *msg)
641 {       
642         char *path, *passwd;
643         char *encoded = NULL;
644
645         passwd = g_hash_table_lookup (passwords, msg->key);
646         if (passwd) {
647                 msg->password = g_strdup(passwd);
648         } else {
649                 /* not part of the session hash, look it up in the on disk db */
650                 path = password_path (msg->component, msg->key);
651                 encoded = gnome_config_private_get_string_with_default (path, NULL);
652                 g_free (path);
653                 if (encoded) {
654                         msg->password = decode_base64 (encoded);
655                         g_free (encoded);
656                 }
657         }
658
659         if (!msg->noreply)
660                 e_msgport_reply(&msg->msg);
661 }
662
663 static void
664 ep_add_password (EPassMsg *msg)
665 {
666         gpointer okey, value;
667
668         if (g_hash_table_lookup_extended (passwords, msg->key, &okey, &value)) {
669                 g_hash_table_remove (passwords, msg->key);
670                 g_free (okey);
671                 g_free (value);
672         }
673
674         g_hash_table_insert (passwords, g_strdup (msg->key), g_strdup (msg->oldpass));
675
676         if (!msg->noreply)
677                 e_msgport_reply(&msg->msg);
678 }
679
680 static void ep_ask_password(EPassMsg *msg);
681
682 static void
683 pass_response(GtkDialog *dialog, int response, void *data)
684 {
685         EPassMsg *msg = data;
686         int type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
687         EDList pending = E_DLIST_INITIALISER(pending);
688         EPassMsg *mw, *mn;
689
690         if (response == GTK_RESPONSE_OK) {
691                 msg->password = g_strdup(gtk_entry_get_text((GtkEntry *)msg->entry));
692
693                 if (type != E_PASSWORDS_REMEMBER_NEVER) {
694                         int noreply = msg->noreply;
695
696                         *msg->remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (msg->check));
697
698                         msg->noreply = 1;
699
700                         if (*msg->remember || type == E_PASSWORDS_REMEMBER_FOREVER) {
701                                 msg->oldpass = msg->password;
702                                 ep_add_password(msg);
703                         }
704 #ifdef WITH_GNOME_KEYRING
705                         if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER) {
706                                 if (gnome_keyring_is_available())
707                                         ep_remember_password_keyring(msg);
708                                 else
709                                         ep_remember_password_file(msg);
710                         }
711 #else
712                         if (*msg->remember && type == E_PASSWORDS_REMEMBER_FOREVER)
713                                 ep_remember_password_file(msg);                             
714 #endif                              
715
716                         msg->noreply = noreply;
717                 }
718         }
719
720         gtk_widget_destroy((GtkWidget *)dialog);
721         password_dialog = NULL;
722
723         /* ok, here things get interesting, we suck up any pending
724          * operations on this specific password, and return the same
725          * result or ignore other operations */
726
727         LOCK();
728         mw = (EPassMsg *)request_list.head;
729         mn = (EPassMsg *)mw->msg.ln.next;
730         while (mn) {
731 #ifdef WITH_GNOME_KEYRING               
732                 if ((mw->dispatch == (gnome_keyring_is_available() ? ep_forget_password_keyring : ep_forget_password_file)
733 #else
734                 if ((mw->dispatch == ep_forget_password_file                 
735 #endif
736 #ifdef WITH_GNOME_KEYRING                                    
737                      || mw->dispatch == (gnome_keyring_is_available() ? ep_get_password_keyring : ep_get_password_file)
738 #else
739                      || mw->dispatch == ep_get_password_file                 
740 #endif
741                      || mw->dispatch == ep_ask_password)                    
742                     && (strcmp(mw->component, msg->component) == 0
743                         && strcmp(mw->key, msg->key) == 0)) {
744                         e_dlist_remove((EDListNode *)mw);
745                         mw->password = g_strdup(msg->password);
746                         e_msgport_reply(&mw->msg);
747                 }
748                 mw = mn;
749                 mn = (EPassMsg *)mn->msg.ln.next;
750         }
751         UNLOCK();
752
753         if (!msg->noreply)
754                 e_msgport_reply(&msg->msg);
755
756         ep_idle_dispatch(NULL);
757 }
758
759 static void
760 ep_ask_password(EPassMsg *msg)
761 {
762         GtkWidget *vbox;
763         int type = msg->flags & E_PASSWORDS_REMEMBER_MASK;
764         guint noreply = msg->noreply;
765         AtkObject *a11y;
766
767         msg->noreply = 1;
768
769         /*password_dialog = (GtkDialog *)e_error_new(msg->parent, "mail:ask-session-password", msg->prompt, NULL);*/
770         password_dialog = (GtkDialog *)gtk_message_dialog_new (msg->parent,
771                                                                0,
772                                                                GTK_MESSAGE_QUESTION,
773                                                                GTK_BUTTONS_OK_CANCEL,
774                                                                "%s", msg->prompt);
775         gtk_window_set_title(GTK_WINDOW(password_dialog), msg->title);
776
777         gtk_widget_ensure_style (GTK_WIDGET (password_dialog));
778         gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (password_dialog)->vbox), 0);
779         gtk_container_set_border_width (GTK_CONTAINER (GTK_DIALOG (password_dialog)->action_area), 12);
780
781         gtk_dialog_set_default_response(password_dialog, GTK_RESPONSE_OK);
782
783         vbox = gtk_vbox_new (FALSE, 12);
784         gtk_widget_show (vbox);
785         gtk_box_pack_start (GTK_BOX (GTK_DIALOG (password_dialog)->vbox), vbox, TRUE, FALSE, 0);
786         gtk_container_set_border_width((GtkContainer *)vbox, 12);
787         
788         msg->entry = gtk_entry_new ();
789
790         a11y = gtk_widget_get_accessible (msg->entry);
791         atk_object_set_description (a11y, msg->prompt);
792         gtk_entry_set_visibility ((GtkEntry *)msg->entry, !(msg->flags & E_PASSWORDS_SECRET));
793         gtk_entry_set_activates_default((GtkEntry *)msg->entry, TRUE);
794         gtk_box_pack_start (GTK_BOX (vbox), msg->entry, TRUE, FALSE, 3);
795         gtk_widget_show (msg->entry);
796         gtk_widget_grab_focus (msg->entry);
797         
798         if ((msg->flags & E_PASSWORDS_REPROMPT)) {
799 #ifdef WITH_GNOME_KEYRING
800                 if (gnome_keyring_is_available())
801                         ep_get_password_keyring(msg);
802                 else
803                         ep_get_password_file(msg);                      
804 #else
805                 ep_get_password_file(msg);
806 #endif
807                 if (msg->password) {
808                         gtk_entry_set_text ((GtkEntry *) msg->entry, msg->password);
809                         g_free (msg->password);
810                         msg->password = NULL;
811                 }
812         }
813
814         /* static password, shouldn't be remembered between sessions,
815            but will be remembered within the session beyond our control */
816         if (type != E_PASSWORDS_REMEMBER_NEVER) {
817                 if (msg->flags & E_PASSWORDS_PASSPHRASE) {
818                         msg->check = gtk_check_button_new_with_mnemonic(type == E_PASSWORDS_REMEMBER_FOREVER
819                                                                         ? _("_Remember this passphrase")
820                                                                         : _("_Remember this passphrase for the remainder of this session"));
821                 } else {
822                         msg->check = gtk_check_button_new_with_mnemonic(type == E_PASSWORDS_REMEMBER_FOREVER
823                                                                         ? _("_Remember this password")
824                                                                         : _("_Remember this password for the remainder of this session"));
825                         
826                 }
827                 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (msg->check), *msg->remember);
828                 gtk_box_pack_start (GTK_BOX (vbox), msg->check, TRUE, FALSE, 3);
829                 if ((msg->flags & E_PASSWORDS_DISABLE_REMEMBER))
830                         gtk_widget_set_sensitive(msg->check, FALSE);
831                 gtk_widget_show (msg->check);
832         }
833         
834         msg->noreply = noreply;
835
836         g_signal_connect(password_dialog, "response", G_CALLBACK (pass_response), msg);
837
838         if (msg->parent)
839                 gtk_dialog_run (GTK_DIALOG (password_dialog));
840         else
841                 gtk_widget_show((GtkWidget *)password_dialog);
842 }
843
844
845 /**
846  * e_passwords_init:
847  *
848  * Initializes the e_passwords routines. Must be called before any other
849  * e_passwords_* function.
850  **/
851 void
852 e_passwords_init (void)
853 {
854         LOCK();
855
856         if (!passwords) {
857                 /* create the per-session hash table */
858                 passwords = g_hash_table_new (g_str_hash, g_str_equal);
859 #ifdef ENABLE_THREADS
860                 main_thread = pthread_self();
861 #endif
862         }
863
864         UNLOCK();
865 }
866
867 /**
868  * e_passwords_cancel:
869  * 
870  * Cancel any outstanding password operations and close any dialogues
871  * currently being shown.
872  **/
873 void
874 e_passwords_cancel(void)
875 {
876         EPassMsg *msg;
877
878         LOCK();
879         while ((msg = (EPassMsg *)e_dlist_remhead(&request_list)))
880                 e_msgport_reply(&msg->msg);
881         UNLOCK();
882
883         if (password_dialog)
884                 gtk_dialog_response(password_dialog,GTK_RESPONSE_CANCEL);
885 }
886
887 /**
888  * e_passwords_shutdown:
889  *
890  * Cleanup routine to call before exiting.
891  **/
892 void
893 e_passwords_shutdown (void)
894 {
895 #ifdef WITH_GNOME_KEYRING
896         /* shouldn't need this really - everything is synchronous */
897         if (!gnome_keyring_is_available())
898                 gnome_config_private_sync_file ("/Evolution");
899 #else
900         gnome_config_private_sync_file ("/Evolution");
901 #endif
902         e_passwords_cancel();
903
904         if (passwords) {
905                 /* and destroy our per session hash */
906                 g_hash_table_foreach_remove (passwords, free_entry, NULL);
907                 g_hash_table_destroy (passwords);
908                 passwords = NULL;
909         }
910 }
911
912 /**
913  * e_passwords_set_online:
914  * @state: 
915  * 
916  * Set the offline-state of the application.  This is a work-around
917  * for having the backends fully offline aware, and returns a
918  * cancellation response instead of prompting for passwords.
919  *
920  * FIXME: This is not a permanent api, review post 2.0.
921  **/
922 void
923 e_passwords_set_online(int state)
924 {
925         ep_online_state = state;
926         /* TODO: we could check that a request is open and close it, or maybe who cares */
927 }
928
929 /**
930  * e_passwords_forget_passwords:
931  *
932  * Forgets all cached passwords, in memory and on disk.
933  **/
934 void
935 e_passwords_forget_passwords (void)
936 {
937 #ifdef WITH_GNOME_KEYRING       
938         EPassMsg *msg = ep_msg_new(gnome_keyring_is_available() ? ep_forget_passwords_keyring : ep_forget_passwords_file);
939 #else
940         EPassMsg *msg = ep_msg_new(ep_forget_passwords_file);
941 #endif
942         
943         ep_msg_send(msg);
944         ep_msg_free(msg);
945 }
946
947 /**
948  * e_passwords_clear_passwords:
949  *
950  * Forgets all disk cached passwords for the component.
951  **/
952 void
953 e_passwords_clear_passwords (const char *component_name)
954 {
955 #ifdef WITH_GNOME_KEYRING       
956         EPassMsg *msg = ep_msg_new(gnome_keyring_is_available() ? ep_clear_passwords_keyring : ep_clear_passwords_file);
957 #else
958         EPassMsg *msg = ep_msg_new(ep_clear_passwords_file);            
959 #endif
960
961         msg->component = component_name;
962         ep_msg_send(msg);
963         ep_msg_free(msg);
964 }
965
966 /**
967  * e_passwords_remember_password:
968  * @key: the key
969  *
970  * Saves the password associated with @key to disk.
971  **/
972 void
973 e_passwords_remember_password (const char *component_name, const char *key)
974 {
975         EPassMsg *msg;
976
977         g_return_if_fail(component_name != NULL);
978         g_return_if_fail(key != NULL);
979
980 #ifdef WITH_GNOME_KEYRING
981         msg = ep_msg_new(gnome_keyring_is_available() ? ep_remember_password_keyring : ep_remember_password_file);
982 #else
983         msg = ep_msg_new(ep_remember_password_file);
984 #endif  
985         msg->component = component_name;
986         msg->key = key;
987
988         ep_msg_send(msg);
989         ep_msg_free(msg);
990 }
991
992 /**
993  * e_passwords_forget_password:
994  * @key: the key
995  *
996  * Forgets the password associated with @key, in memory and on disk.
997  **/
998 void
999 e_passwords_forget_password (const char *component_name, const char *key)
1000 {
1001         EPassMsg *msg;
1002
1003         g_return_if_fail(component_name != NULL);
1004         g_return_if_fail(key != NULL);
1005
1006 #ifdef WITH_GNOME_KEYRING       
1007         msg = ep_msg_new(gnome_keyring_is_available() ? ep_forget_password_keyring : ep_forget_password_file);
1008 #else   
1009         msg = ep_msg_new(ep_forget_password_file);
1010 #endif  
1011         msg->component = component_name;
1012         msg->key = key;
1013
1014         ep_msg_send(msg);
1015         ep_msg_free(msg);
1016 }
1017
1018 /**
1019  * e_passwords_get_password:
1020  * @key: the key
1021  *
1022  * Return value: the password associated with @key, or %NULL.  Caller
1023  * must free the returned password.
1024  **/
1025 char *
1026 e_passwords_get_password (const char *component_name, const char *key)
1027 {
1028         EPassMsg *msg;
1029         char *passwd;
1030
1031         g_return_val_if_fail(component_name != NULL, NULL);
1032         g_return_val_if_fail(key != NULL, NULL);
1033
1034 #ifdef WITH_GNOME_KEYRING       
1035         msg = ep_msg_new(gnome_keyring_is_available() ? ep_get_password_keyring : ep_get_password_file);
1036 #else
1037         msg = ep_msg_new(ep_get_password_file);
1038 #endif  
1039
1040         msg->component = component_name;
1041         msg->key = key;
1042
1043         ep_msg_send(msg);
1044
1045         passwd = msg->password;
1046         msg->password = NULL;
1047         ep_msg_free(msg);
1048
1049         return passwd;
1050 }
1051
1052 /**
1053  * e_passwords_add_password:
1054  * @key: a key
1055  * @passwd: the password for @key
1056  *
1057  * This stores the @key/@passwd pair in the current session's password
1058  * hash.
1059  **/
1060 void
1061 e_passwords_add_password (const char *key, const char *passwd)
1062 {
1063         EPassMsg *msg;
1064
1065         g_return_if_fail(key != NULL);
1066         g_return_if_fail(passwd != NULL);
1067
1068         msg = ep_msg_new(ep_add_password);
1069         msg->key = key;
1070         msg->oldpass = passwd;
1071
1072         ep_msg_send(msg);
1073         ep_msg_free(msg);
1074 }
1075
1076 /**
1077  * e_passwords_ask_password:
1078  * @title: title for the password dialog
1079  * @component_name: the name of the component for which we're storing
1080  * the password (e.g. Mail, Addressbook, etc.)
1081  * @key: key to store the password under
1082  * @prompt: prompt string
1083  * @type: whether or not to offer to remember the password,
1084  * and for how long.
1085  * @remember: on input, the default state of the remember checkbox.
1086  * on output, the state of the checkbox when the dialog was closed.
1087  * @parent: parent window of the dialog, or %NULL
1088  *
1089  * Asks the user for a password.
1090  *
1091  * Return value: the password, which the caller must free, or %NULL if
1092  * the user cancelled the operation. *@remember will be set if the
1093  * return value is non-%NULL and @remember_type is not
1094  * E_PASSWORDS_DO_NOT_REMEMBER.
1095  **/
1096 char *
1097 e_passwords_ask_password (const char *title, const char *component_name,
1098                           const char *key,
1099                           const char *prompt,
1100                           EPasswordsRememberType type,
1101                           gboolean *remember,
1102                           GtkWindow *parent)
1103 {
1104         char *passwd;
1105         EPassMsg *msg = ep_msg_new(ep_ask_password);
1106
1107         if ((type & E_PASSWORDS_ONLINE) && !ep_online_state)
1108                 return NULL;
1109
1110         msg->title = title;
1111         msg->component = component_name;
1112         msg->key = key;
1113         msg->prompt = prompt;
1114         msg->flags = type;
1115         msg->remember = remember;
1116         msg->parent = parent;
1117
1118         ep_msg_send(msg);
1119         passwd = msg->password;
1120         msg->password = NULL;
1121         ep_msg_free(msg);
1122         
1123         return passwd;
1124 }
1125
1126
1127
1128 static char *base64_alphabet =
1129 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1130
1131 static unsigned char camel_mime_base64_rank[256] = {
1132         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1133         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1134         255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
1135          52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
1136         255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
1137          15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
1138         255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
1139          41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
1140         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1141         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1142         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1143         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1144         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1145         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1146         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1147         255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
1148 };
1149
1150 /* call this when finished encoding everything, to
1151    flush off the last little bit */
1152 static int
1153 base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
1154 {
1155         int c1, c2;
1156         unsigned char *outptr = out;
1157
1158         if (inlen>0)
1159                 outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
1160
1161         c1 = ((unsigned char *)save)[1];
1162         c2 = ((unsigned char *)save)[2];
1163         
1164         switch (((char *)save)[0]) {
1165         case 2:
1166                 outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
1167                 g_assert(outptr[2] != 0);
1168                 goto skip;
1169         case 1:
1170                 outptr[2] = '=';
1171         skip:
1172                 outptr[0] = base64_alphabet[ c1 >> 2 ];
1173                 outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
1174                 outptr[3] = '=';
1175                 outptr += 4;
1176                 break;
1177         }
1178         if (break_lines)
1179                 *outptr++ = '\n';
1180
1181         *save = 0;
1182         *state = 0;
1183
1184         return outptr-out;
1185 }
1186
1187 /*
1188   performs an 'encode step', only encodes blocks of 3 characters to the
1189   output at a time, saves left-over state in state and save (initialise to
1190   0 on first invocation).
1191 */
1192 static int
1193 base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save)
1194 {
1195         register unsigned char *inptr, *outptr;
1196
1197         if (len<=0)
1198                 return 0;
1199
1200         inptr = in;
1201         outptr = out;
1202
1203         if (len + ((char *)save)[0] > 2) {
1204                 unsigned char *inend = in+len-2;
1205                 register int c1, c2, c3;
1206                 register int already;
1207
1208                 already = *state;
1209
1210                 switch (((char *)save)[0]) {
1211                 case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
1212                 case 2: c1 = ((unsigned char *)save)[1];
1213                         c2 = ((unsigned char *)save)[2]; goto skip2;
1214                 }
1215                 
1216                 /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
1217                 while (inptr < inend) {
1218                         c1 = *inptr++;
1219                 skip1:
1220                         c2 = *inptr++;
1221                 skip2:
1222                         c3 = *inptr++;
1223                         *outptr++ = base64_alphabet[ c1 >> 2 ];
1224                         *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
1225                         *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
1226                         *outptr++ = base64_alphabet[ c3 & 0x3f ];
1227                         /* this is a bit ugly ... */
1228                         if (break_lines && (++already)>=19) {
1229                                 *outptr++='\n';
1230                                 already = 0;
1231                         }
1232                 }
1233
1234                 ((char *)save)[0] = 0;
1235                 len = 2-(inptr-inend);
1236                 *state = already;
1237         }
1238
1239         if (len>0) {
1240                 register char *saveout;
1241
1242                 /* points to the slot for the next char to save */
1243                 saveout = & (((char *)save)[1]) + ((char *)save)[0];
1244
1245                 /* len can only be 0 1 or 2 */
1246                 switch(len) {
1247                 case 2: *saveout++ = *inptr++;
1248                 case 1: *saveout++ = *inptr++;
1249                 }
1250                 ((char *)save)[0]+=len;
1251         }
1252
1253         return outptr-out;
1254 }
1255
1256
1257 /**
1258  * base64_decode_step: decode a chunk of base64 encoded data
1259  * @in: input stream
1260  * @len: max length of data to decode
1261  * @out: output stream
1262  * @state: holds the number of bits that are stored in @save
1263  * @save: leftover bits that have not yet been decoded
1264  *
1265  * Decodes a chunk of base64 encoded data
1266  **/
1267 static int
1268 base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save)
1269 {
1270         register unsigned char *inptr, *outptr;
1271         unsigned char *inend, c;
1272         register unsigned int v;
1273         int i;
1274
1275         inend = in+len;
1276         outptr = out;
1277
1278         /* convert 4 base64 bytes to 3 normal bytes */
1279         v=*save;
1280         i=*state;
1281         inptr = in;
1282         while (inptr<inend) {
1283                 c = camel_mime_base64_rank[*inptr++];
1284                 if (c != 0xff) {
1285                         v = (v<<6) | c;
1286                         i++;
1287                         if (i==4) {
1288                                 *outptr++ = v>>16;
1289                                 *outptr++ = v>>8;
1290                                 *outptr++ = v;
1291                                 i=0;
1292                         }
1293                 }
1294         }
1295
1296         *save = v;
1297         *state = i;
1298
1299         /* quick scan back for '=' on the end somewhere */
1300         /* fortunately we can drop 1 output char for each trailing = (upto 2) */
1301         i=2;
1302         while (inptr>in && i) {
1303                 inptr--;
1304                 if (camel_mime_base64_rank[*inptr] != 0xff) {
1305                         if (*inptr == '=')
1306                                 outptr--;
1307                         i--;
1308                 }
1309         }
1310
1311         /* if i!= 0 then there is a truncation error! */
1312         return outptr-out;
1313 }
1314
1315 static char *
1316 decode_base64 (char *base64)
1317 {
1318         guchar *plain;
1319         char *pad = "==";
1320         int len, out, state;
1321         unsigned int save;
1322         
1323         len = strlen (base64);
1324         plain = g_malloc0 (len);
1325         state = save = 0;
1326         out = base64_decode_step ((guchar *)base64, len, plain, &state, &save);
1327         if (len % 4) {
1328                 base64_decode_step ((guchar *)pad, 4 - len % 4, plain + out,
1329                                     &state, &save);
1330         }
1331         
1332         return (char *)plain;
1333 }