source: trunk/third/evolution/e-util/e-passwords.c @ 18142

Revision 18142, 15.0 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18141, which included commits to RCS files with non-trunk default branches.
Line 
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 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 General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 * USA.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28
29#include "e-passwords.h"
30#include <libgnome/gnome-defs.h>
31#include <libgnome/gnome-i18n.h>
32#include <libgnome/gnome-config.h>
33#include <libgnomeui/gnome-dialog.h>
34#include <libgnomeui/gnome-messagebox.h>
35#include <libgnomeui/gnome-stock.h>
36#include <gtk/gtkentry.h>
37#include <gtk/gtkcheckbutton.h>
38#include <bonobo-conf/bonobo-config-database.h>
39#include <bonobo/bonobo-object.h>
40#include <bonobo/bonobo-moniker-util.h>
41#include <bonobo/bonobo-exception.h>
42
43static char *decode_base64 (char *base64);
44
45Bonobo_ConfigDatabase db;
46static GHashTable *passwords = NULL;
47static char *component_name = NULL;
48
49static int base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save);
50static int base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save);
51
52/**
53 * e_passwords_init:
54 *
55 * Initializes the e_passwords routines. Must be called before any other
56 * e_passwords_* function.
57 **/
58void
59e_passwords_init (const char *component)
60{
61        CORBA_Environment ev;
62
63        /* open up our bonobo config database */
64        CORBA_exception_init (&ev);
65        db = bonobo_get_object ("wombat-private:", "Bonobo/ConfigDatabase", &ev);
66
67        if (BONOBO_EX (&ev) || db == CORBA_OBJECT_NIL) {
68                char *err;
69                g_error ("Very serious error, cannot activate private config database '%s'",
70                         (err = bonobo_exception_get_text (&ev)));
71                g_free (err);
72                CORBA_exception_free (&ev);
73                return;
74        }
75
76        CORBA_exception_free (&ev);
77
78        /* and create the per-session hash table */
79        passwords = g_hash_table_new (g_str_hash, g_str_equal);
80
81        component_name = g_strdup (component);
82}
83
84static gboolean
85free_entry (gpointer key, gpointer value, gpointer user_data)
86{
87        g_free (key);
88        memset (value, 0, strlen (value));
89        g_free (value);
90        return TRUE;
91}
92
93/**
94 * e_passwords_shutdown:
95 *
96 * Cleanup routine to call before exiting.
97 **/
98void
99e_passwords_shutdown ()
100{
101        CORBA_Environment ev;
102
103        /* sync our db work */
104        CORBA_exception_init (&ev);
105        Bonobo_ConfigDatabase_sync (db, &ev);
106        bonobo_object_release_unref (db, &ev);
107        CORBA_exception_free (&ev);
108        db = NULL;
109
110        /* and destroy our per session hash */
111        g_hash_table_foreach_remove (passwords, free_entry, NULL);
112        g_hash_table_destroy (passwords);
113        passwords = NULL;
114
115        g_free (component_name);
116        component_name = NULL;
117}
118
119
120/**
121 * e_passwords_forget_passwords:
122 *
123 * Forgets all cached passwords, in memory and on disk.
124 **/
125void
126e_passwords_forget_passwords ()
127{
128        CORBA_Environment ev;
129
130        /* remove all the persistent passwords */
131        CORBA_exception_init (&ev);
132        Bonobo_ConfigDatabase_removeDir (db, "/Passwords", &ev);
133        Bonobo_ConfigDatabase_sync (db, &ev);
134        CORBA_exception_free (&ev);
135
136        /* free up the session passwords */
137        g_hash_table_foreach_remove (passwords, free_entry, NULL);
138}
139
140/**
141 * e_passwords_clear_component_passwords:
142 *
143 * Forgets all disk cached passwords.
144 **/
145void
146e_passwords_clear_component_passwords ()
147{
148        CORBA_Environment ev;
149        char *path;
150
151        path = g_strdup_printf ("/Passwords/%s", component_name);
152
153        CORBA_exception_init (&ev);
154        Bonobo_ConfigDatabase_removeDir (db, path, &ev);
155        Bonobo_ConfigDatabase_sync (db, &ev);
156        CORBA_exception_free (&ev);
157
158        g_free (path);
159}
160
161static char *
162password_path (const char *key)
163{
164        int len, state, save;
165        char *key64, *path;
166
167        len = strlen (key);
168        key64 = g_malloc0 ((len + 2) * 4 / 3 + 1);
169        state = save = 0;
170        base64_encode_close ((char*)key, len, FALSE, key64, &state, &save);
171        path = g_strdup_printf ("/Passwords/%s/%s", component_name, key64);
172        g_free (key64);
173
174        return path;
175}
176
177/**
178 * e_passwords_remember_password:
179 * @key: the key
180 *
181 * Saves the password associated with @key to disk.
182 **/
183void
184e_passwords_remember_password (const char *key)
185{
186        gpointer okey, value;
187        char *path, *pass64;
188        int len, state, save;
189
190        if (!g_hash_table_lookup_extended (passwords, key, &okey, &value))
191                return;
192
193        /* add it to the on-disk cache of passwords */
194        path = password_path (okey);
195
196        len = strlen (value);
197        pass64 = g_malloc0 ((len + 2) * 4 / 3 + 1);
198        state = save = 0;
199        base64_encode_close (value, len, FALSE, pass64, &state, &save);
200
201        bonobo_config_set_string (db, path, pass64, NULL);
202        g_free (path);
203        g_free (pass64);
204
205        /* now remove it from our session hash */
206        g_hash_table_remove (passwords, key);
207        g_free (okey);
208        g_free (value);
209}
210
211/**
212 * e_passwords_forget_password:
213 * @key: the key
214 *
215 * Forgets the password associated with @key, in memory and on disk.
216 **/
217void
218e_passwords_forget_password (const char *key)
219{
220        gpointer okey, value;
221        CORBA_Environment ev;
222        char *path;
223
224        if (g_hash_table_lookup_extended (passwords, key, &okey, &value)) {
225                g_hash_table_remove (passwords, key);
226                memset (value, 0, strlen (value));
227                g_free (okey);
228                g_free (value);
229        }
230
231        /* clear it in the on disk db */
232        path = password_path (key);
233        CORBA_exception_init (&ev);
234        Bonobo_ConfigDatabase_removeValue (db, path, &ev);
235        CORBA_exception_free (&ev);
236        g_free (path);
237}
238
239/**
240 * e_passwords_get_password:
241 * @key: the key
242 *
243 * Return value: the password associated with @key, or %NULL.  Caller
244 * must free the returned password.
245 **/
246char *
247e_passwords_get_password (const char *key)
248{
249        char *path, *passwd = g_hash_table_lookup (passwords, key);
250        CORBA_Environment ev;
251        char *encoded;
252       
253        if (passwd)
254                return g_strdup (passwd);
255       
256        /* not part of the session hash, look it up in the on disk db */
257        path = password_path (key);
258       
259        /* We need to pass an ev to bonobo-conf, or it will emit a
260         * g_warning if the data isn't found.
261         */
262        CORBA_exception_init (&ev);
263        encoded = bonobo_config_get_string (db, path, &ev);
264        CORBA_exception_free (&ev);
265       
266        g_free (path);
267       
268        if (!encoded)
269                return NULL;
270       
271        passwd = decode_base64 (encoded);
272        g_free (encoded);
273       
274        return passwd;
275}
276
277/**
278 * e_passwords_add_password:
279 * @key: a key
280 * @passwd: the password for @key
281 *
282 * This stores the @key/@passwd pair in the current session's password
283 * hash.
284 **/
285void
286e_passwords_add_password (const char *key, const char *passwd)
287{
288        gpointer okey, value;
289
290        /* FIXME: shouldn't this be g_return_if_fail? */
291        if (!key || !passwd)
292                return;
293
294        if (g_hash_table_lookup_extended (passwords, key, &okey, &value)) {
295                g_hash_table_remove (passwords, key);
296                g_free (okey);
297                g_free (value);
298        }
299
300        g_hash_table_insert (passwords, g_strdup (key), g_strdup (passwd));
301}
302
303
304/**
305 * e_passwords_ask_password:
306 * @title: title for the password dialog
307 * @key: key to store the password under
308 * @prompt: prompt string
309 * @secret: whether or not the password text should be ***ed out
310 * @remember_type: whether or not to offer to remember the password,
311 * and for how long.
312 * @remember: on input, the default state of the remember checkbox.
313 * on output, the state of the checkbox when the dialog was closed.
314 * @parent: parent window of the dialog, or %NULL
315 *
316 * Asks the user for a password.
317 *
318 * Return value: the password, which the caller must free, or %NULL if
319 * the user cancelled the operation. *@remember will be set if the
320 * return value is non-%NULL and @remember_type is not
321 * E_PASSWORDS_DO_NOT_REMEMBER.
322 **/
323char *
324e_passwords_ask_password (const char *title, const char *key,
325                          const char *prompt, gboolean secret,
326                          EPasswordsRememberType remember_type,
327                          gboolean *remember,
328                          GtkWindow *parent)
329{
330        GtkWidget *dialog;
331        GtkWidget *check = NULL, *entry;
332        char *password;
333        int button;
334
335        dialog = gnome_message_box_new (prompt, GNOME_MESSAGE_BOX_QUESTION,
336                                        GNOME_STOCK_BUTTON_OK,
337                                        GNOME_STOCK_BUTTON_CANCEL,
338                                        NULL);
339        gtk_window_set_title (GTK_WINDOW (dialog), title);
340        if (parent)
341                gnome_dialog_set_parent (GNOME_DIALOG (dialog), parent);
342        gnome_dialog_set_default (GNOME_DIALOG (dialog), 0);
343        gnome_dialog_set_close (GNOME_DIALOG (dialog), FALSE);
344
345        /* Password entry */
346        entry = gtk_entry_new();
347        if (secret)
348                gtk_entry_set_visibility (GTK_ENTRY(entry), FALSE);
349
350        gtk_box_pack_start (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
351                            entry, FALSE, FALSE, 4);
352        gtk_widget_show (entry);
353        gtk_widget_grab_focus (entry);
354
355        /* If Return is pressed in the text entry, propagate to the buttons */
356        gnome_dialog_editable_enters (GNOME_DIALOG(dialog), GTK_EDITABLE(entry));
357
358        /* Remember the password? */
359        if (remember_type != E_PASSWORDS_DO_NOT_REMEMBER) {
360                const char *label;
361
362                if (remember_type == E_PASSWORDS_REMEMBER_FOREVER)
363                        label = _("Remember this password");
364                else
365                        label = _("Remember this password for the remainder of this session");
366                check = gtk_check_button_new_with_label (label);
367                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check),
368                                              *remember);
369
370                gtk_box_pack_end (GTK_BOX (GNOME_DIALOG (dialog)->vbox),
371                                  check, TRUE, FALSE, 4);
372                gtk_widget_show (check);
373        }
374
375        gtk_widget_show (dialog);
376        button = gnome_dialog_run (GNOME_DIALOG (dialog));
377
378        if (button == 0) {
379                password = gtk_editable_get_chars (GTK_EDITABLE (entry), 0, -1);
380                if (remember_type != E_PASSWORDS_DO_NOT_REMEMBER) {
381                        *remember = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (check));
382
383                        if (*remember || remember_type == E_PASSWORDS_REMEMBER_FOREVER)
384                                e_passwords_add_password (key, password);
385                        if (*remember && remember_type == E_PASSWORDS_REMEMBER_FOREVER)
386                                e_passwords_remember_password (key);
387                }
388        } else
389                password = NULL;
390
391        gnome_dialog_close (GNOME_DIALOG (dialog));
392        return password;
393}
394
395
396
397static char *base64_alphabet =
398"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
399
400static unsigned char camel_mime_base64_rank[256] = {
401        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
402        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
403        255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63,
404         52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,  0,255,255,
405        255,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
406         15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
407        255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
408         41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
409        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
410        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
411        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
412        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
413        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
414        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
415        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
416        255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
417};
418
419/* call this when finished encoding everything, to
420   flush off the last little bit */
421static int
422base64_encode_close(unsigned char *in, int inlen, gboolean break_lines, unsigned char *out, int *state, int *save)
423{
424        int c1, c2;
425        unsigned char *outptr = out;
426
427        if (inlen>0)
428                outptr += base64_encode_step(in, inlen, break_lines, outptr, state, save);
429
430        c1 = ((unsigned char *)save)[1];
431        c2 = ((unsigned char *)save)[2];
432       
433        switch (((char *)save)[0]) {
434        case 2:
435                outptr[2] = base64_alphabet[ ( (c2 &0x0f) << 2 ) ];
436                g_assert(outptr[2] != 0);
437                goto skip;
438        case 1:
439                outptr[2] = '=';
440        skip:
441                outptr[0] = base64_alphabet[ c1 >> 2 ];
442                outptr[1] = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 )];
443                outptr[3] = '=';
444                outptr += 4;
445                break;
446        }
447        if (break_lines)
448                *outptr++ = '\n';
449
450        *save = 0;
451        *state = 0;
452
453        return outptr-out;
454}
455
456/*
457  performs an 'encode step', only encodes blocks of 3 characters to the
458  output at a time, saves left-over state in state and save (initialise to
459  0 on first invocation).
460*/
461static int
462base64_encode_step(unsigned char *in, int len, gboolean break_lines, unsigned char *out, int *state, int *save)
463{
464        register unsigned char *inptr, *outptr;
465
466        if (len<=0)
467                return 0;
468
469        inptr = in;
470        outptr = out;
471
472        if (len + ((char *)save)[0] > 2) {
473                unsigned char *inend = in+len-2;
474                register int c1, c2, c3;
475                register int already;
476
477                already = *state;
478
479                switch (((char *)save)[0]) {
480                case 1: c1 = ((unsigned char *)save)[1]; goto skip1;
481                case 2: c1 = ((unsigned char *)save)[1];
482                        c2 = ((unsigned char *)save)[2]; goto skip2;
483                }
484               
485                /* yes, we jump into the loop, no i'm not going to change it, it's beautiful! */
486                while (inptr < inend) {
487                        c1 = *inptr++;
488                skip1:
489                        c2 = *inptr++;
490                skip2:
491                        c3 = *inptr++;
492                        *outptr++ = base64_alphabet[ c1 >> 2 ];
493                        *outptr++ = base64_alphabet[ c2 >> 4 | ( (c1&0x3) << 4 ) ];
494                        *outptr++ = base64_alphabet[ ( (c2 &0x0f) << 2 ) | (c3 >> 6) ];
495                        *outptr++ = base64_alphabet[ c3 & 0x3f ];
496                        /* this is a bit ugly ... */
497                        if (break_lines && (++already)>=19) {
498                                *outptr++='\n';
499                                already = 0;
500                        }
501                }
502
503                ((char *)save)[0] = 0;
504                len = 2-(inptr-inend);
505                *state = already;
506        }
507
508        if (len>0) {
509                register char *saveout;
510
511                /* points to the slot for the next char to save */
512                saveout = & (((char *)save)[1]) + ((char *)save)[0];
513
514                /* len can only be 0 1 or 2 */
515                switch(len) {
516                case 2: *saveout++ = *inptr++;
517                case 1: *saveout++ = *inptr++;
518                }
519                ((char *)save)[0]+=len;
520        }
521
522        return outptr-out;
523}
524
525
526/**
527 * base64_decode_step: decode a chunk of base64 encoded data
528 * @in: input stream
529 * @len: max length of data to decode
530 * @out: output stream
531 * @state: holds the number of bits that are stored in @save
532 * @save: leftover bits that have not yet been decoded
533 *
534 * Decodes a chunk of base64 encoded data
535 **/
536static int
537base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save)
538{
539        register unsigned char *inptr, *outptr;
540        unsigned char *inend, c;
541        register unsigned int v;
542        int i;
543
544        inend = in+len;
545        outptr = out;
546
547        /* convert 4 base64 bytes to 3 normal bytes */
548        v=*save;
549        i=*state;
550        inptr = in;
551        while (inptr<inend) {
552                c = camel_mime_base64_rank[*inptr++];
553                if (c != 0xff) {
554                        v = (v<<6) | c;
555                        i++;
556                        if (i==4) {
557                                *outptr++ = v>>16;
558                                *outptr++ = v>>8;
559                                *outptr++ = v;
560                                i=0;
561                        }
562                }
563        }
564
565        *save = v;
566        *state = i;
567
568        /* quick scan back for '=' on the end somewhere */
569        /* fortunately we can drop 1 output char for each trailing = (upto 2) */
570        i=2;
571        while (inptr>in && i) {
572                inptr--;
573                if (camel_mime_base64_rank[*inptr] != 0xff) {
574                        if (*inptr == '=')
575                                outptr--;
576                        i--;
577                }
578        }
579
580        /* if i!= 0 then there is a truncation error! */
581        return outptr-out;
582}
583
584static char *
585decode_base64 (char *base64)
586{
587        char *plain, *pad = "==";
588        int len, out, state, save;
589       
590        len = strlen (base64);
591        plain = g_malloc0 (len);
592        state = save = 0;
593        out = base64_decode_step (base64, len, plain, &state, &save);
594        if (len % 4) {
595                base64_decode_step (pad, 4 - len % 4, plain + out,
596                                    &state, &save);
597        }
598       
599        return plain;
600}
Note: See TracBrowser for help on using the repository browser.