Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / camel-certdb.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  *  Authors: Jeffrey Stedfast <fejj@ximian.com>
4  *
5  *  Copyright 2002-2003 Ximian, Inc. (www.ximian.com)
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU Lesser General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <pthread.h>
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36
37 #include <glib.h>
38 #include <glib/gstdio.h>
39
40 #include <libedataserver/e-memory.h>
41
42 #include "camel-certdb.h"
43 #include "camel-file-utils.h"
44 #include "camel-private.h"
45
46 #define CAMEL_CERTDB_GET_CLASS(db)  ((CamelCertDBClass *) CAMEL_OBJECT_GET_CLASS (db))
47
48 #define CAMEL_CERTDB_VERSION  0x100
49
50 static void camel_certdb_class_init (CamelCertDBClass *klass);
51 static void camel_certdb_init       (CamelCertDB *certdb);
52 static void camel_certdb_finalize   (CamelObject *obj);
53
54 static int certdb_header_load (CamelCertDB *certdb, FILE *istream);
55 static int certdb_header_save (CamelCertDB *certdb, FILE *ostream);
56 static CamelCert *certdb_cert_load (CamelCertDB *certdb, FILE *istream);
57 static int certdb_cert_save (CamelCertDB *certdb, CamelCert *cert, FILE *ostream);
58 static CamelCert *certdb_cert_new (CamelCertDB *certdb);
59 static void certdb_cert_free (CamelCertDB *certdb, CamelCert *cert);
60
61 static const char *cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string);
62 static void cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value);
63
64
65 static CamelObjectClass *parent_class = NULL;
66
67
68 CamelType
69 camel_certdb_get_type (void)
70 {
71         static CamelType type = CAMEL_INVALID_TYPE;
72         
73         if (type == CAMEL_INVALID_TYPE) {
74                 type = camel_type_register (camel_object_get_type (),
75                                             "CamelCertDB",
76                                             sizeof (CamelCertDB),
77                                             sizeof (CamelCertDBClass),
78                                             (CamelObjectClassInitFunc) camel_certdb_class_init,
79                                             NULL,
80                                             (CamelObjectInitFunc) camel_certdb_init,
81                                             (CamelObjectFinalizeFunc) camel_certdb_finalize);
82         }
83         
84         return type;
85 }
86
87
88 static void
89 camel_certdb_class_init (CamelCertDBClass *klass)
90 {
91         parent_class = camel_type_get_global_classfuncs (camel_object_get_type ());
92         
93         klass->header_load = certdb_header_load;
94         klass->header_save = certdb_header_save;
95         
96         klass->cert_new  = certdb_cert_new;
97         klass->cert_load = certdb_cert_load;
98         klass->cert_save = certdb_cert_save;
99         klass->cert_free = certdb_cert_free;
100         klass->cert_get_string = cert_get_string;
101         klass->cert_set_string = cert_set_string;
102 }
103
104 static void
105 camel_certdb_init (CamelCertDB *certdb)
106 {
107         certdb->priv = g_malloc (sizeof (struct _CamelCertDBPrivate));
108         
109         certdb->filename = NULL;
110         certdb->version = CAMEL_CERTDB_VERSION;
111         certdb->saved_certs = 0;
112         
113         certdb->cert_size = sizeof (CamelCert);
114         
115         certdb->cert_chunks = NULL;
116         
117         certdb->certs = g_ptr_array_new ();
118         certdb->cert_hash = g_hash_table_new (g_str_hash, g_str_equal);
119         
120         certdb->priv->db_lock = g_mutex_new ();
121         certdb->priv->io_lock = g_mutex_new ();
122         certdb->priv->alloc_lock = g_mutex_new ();
123         certdb->priv->ref_lock = g_mutex_new ();
124 }
125
126 static void
127 camel_certdb_finalize (CamelObject *obj)
128 {
129         CamelCertDB *certdb = (CamelCertDB *) obj;
130         struct _CamelCertDBPrivate *p;
131         
132         p = certdb->priv;
133         
134         if (certdb->flags & CAMEL_CERTDB_DIRTY)
135                 camel_certdb_save (certdb);
136         
137         camel_certdb_clear (certdb);
138         g_ptr_array_free (certdb->certs, TRUE);
139         g_hash_table_destroy (certdb->cert_hash);
140         
141         g_free (certdb->filename);
142         
143         if (certdb->cert_chunks)
144                 e_memchunk_destroy (certdb->cert_chunks);
145         
146         g_mutex_free (p->db_lock);
147         g_mutex_free (p->io_lock);
148         g_mutex_free (p->alloc_lock);
149         g_mutex_free (p->ref_lock);
150         
151         g_free (p);
152 }
153
154
155 CamelCertDB *
156 camel_certdb_new (void)
157 {
158         return (CamelCertDB *) camel_object_new (camel_certdb_get_type ());
159 }
160
161
162 static CamelCertDB *default_certdb = NULL;
163 static pthread_mutex_t default_certdb_lock = PTHREAD_MUTEX_INITIALIZER;
164
165
166 void
167 camel_certdb_set_default (CamelCertDB *certdb)
168 {
169         pthread_mutex_lock (&default_certdb_lock);
170         
171         if (default_certdb)
172                 camel_object_unref (default_certdb);
173         
174         if (certdb)
175                 camel_object_ref (certdb);
176         
177         default_certdb = certdb;
178         
179         pthread_mutex_unlock (&default_certdb_lock);
180 }
181
182
183 CamelCertDB *
184 camel_certdb_get_default (void)
185 {
186         CamelCertDB *certdb;
187         
188         pthread_mutex_lock (&default_certdb_lock);
189         
190         if (default_certdb)
191                 camel_object_ref (default_certdb);
192         
193         certdb = default_certdb;
194         
195         pthread_mutex_unlock (&default_certdb_lock);
196         
197         return certdb;
198 }
199
200
201 void
202 camel_certdb_set_filename (CamelCertDB *certdb, const char *filename)
203 {
204         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
205         g_return_if_fail (filename != NULL);
206         
207         CAMEL_CERTDB_LOCK (certdb, db_lock);
208         
209         g_free (certdb->filename);
210         certdb->filename = g_strdup (filename);
211         
212         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
213 }
214
215
216 static int
217 certdb_header_load (CamelCertDB *certdb, FILE *istream)
218 {
219         if (camel_file_util_decode_uint32 (istream, &certdb->version) == -1)
220                 return -1;
221         if (camel_file_util_decode_uint32 (istream, &certdb->saved_certs) == -1)
222                 return -1;
223         
224         return 0;
225 }
226
227 static CamelCert *
228 certdb_cert_load (CamelCertDB *certdb, FILE *istream)
229 {
230         CamelCert *cert;
231         
232         cert = camel_certdb_cert_new (certdb);
233
234         if (camel_file_util_decode_string (istream, &cert->issuer) == -1)
235                 goto error;
236         if (camel_file_util_decode_string (istream, &cert->subject) == -1)
237                 goto error;
238         if (camel_file_util_decode_string (istream, &cert->hostname) == -1)
239                 goto error;
240         if (camel_file_util_decode_string (istream, &cert->fingerprint) == -1)
241                 goto error;
242         if (camel_file_util_decode_uint32 (istream, &cert->trust) == -1)
243                 goto error;
244         
245         return cert;
246         
247  error:
248         
249         camel_certdb_cert_unref (certdb, cert);
250         
251         return NULL;
252 }
253
254 int
255 camel_certdb_load (CamelCertDB *certdb)
256 {
257         CamelCert *cert;
258         FILE *in;
259         int i;
260         
261         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
262         g_return_val_if_fail (certdb->filename, -1);
263
264         in = g_fopen (certdb->filename, "rb");
265         if (in == NULL)
266                 return -1;
267         
268         CAMEL_CERTDB_LOCK (certdb, io_lock);
269         if (CAMEL_CERTDB_GET_CLASS (certdb)->header_load (certdb, in) == -1)
270                 goto error;
271         
272         for (i = 0; i < certdb->saved_certs; i++) {
273                 cert = CAMEL_CERTDB_GET_CLASS (certdb)->cert_load (certdb, in);
274                 
275                 if (cert == NULL)
276                         goto error;
277                 
278                 camel_certdb_add (certdb, cert);
279         }
280         
281         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
282         
283         if (fclose (in) != 0)
284                 return -1;
285         
286         certdb->flags &= ~CAMEL_CERTDB_DIRTY;
287         
288         return 0;
289         
290  error:
291         
292         g_warning ("Cannot load certificate database: %s", strerror (ferror (in)));
293         
294         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
295         
296         fclose (in);
297         
298         return -1;
299 }
300
301 static int
302 certdb_header_save (CamelCertDB *certdb, FILE *ostream)
303 {
304         if (camel_file_util_encode_uint32 (ostream, certdb->version) == -1)
305                 return -1;
306         if (camel_file_util_encode_uint32 (ostream, certdb->saved_certs) == -1)
307                 return -1;
308         
309         return 0;
310 }
311
312 static int
313 certdb_cert_save (CamelCertDB *certdb, CamelCert *cert, FILE *ostream)
314 {
315         if (camel_file_util_encode_string (ostream, cert->issuer) == -1)
316                 return -1;
317         if (camel_file_util_encode_string (ostream, cert->subject) == -1)
318                 return -1;
319         if (camel_file_util_encode_string (ostream, cert->hostname) == -1)
320                 return -1;
321         if (camel_file_util_encode_string (ostream, cert->fingerprint) == -1)
322                 return -1;
323         if (camel_file_util_encode_uint32 (ostream, cert->trust) == -1)
324                 return -1;
325         
326         return 0;
327 }
328
329 int
330 camel_certdb_save (CamelCertDB *certdb)
331 {
332         CamelCert *cert;
333         char *filename;
334         int fd, i;
335         FILE *out;
336         
337         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
338         g_return_val_if_fail (certdb->filename, -1);
339         
340         filename = alloca (strlen (certdb->filename) + 4);
341         sprintf (filename, "%s~", certdb->filename);
342         
343         fd = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
344         if (fd == -1)
345                 return -1;
346         
347         out = fdopen (fd, "wb");
348         if (out == NULL) {
349                 i = errno;
350                 close (fd);
351                 g_unlink (filename);
352                 errno = i;
353                 return -1;
354         }
355         
356         CAMEL_CERTDB_LOCK (certdb, io_lock);
357         
358         certdb->saved_certs = certdb->certs->len;
359         if (CAMEL_CERTDB_GET_CLASS (certdb)->header_save (certdb, out) == -1)
360                 goto error;
361         
362         for (i = 0; i < certdb->saved_certs; i++) {
363                 cert = (CamelCert *) certdb->certs->pdata[i];
364                 
365                 if (CAMEL_CERTDB_GET_CLASS (certdb)->cert_save (certdb, cert, out) == -1)
366                         goto error;
367         }
368         
369         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
370         
371         if (fflush (out) != 0 || fsync (fileno (out)) == -1) {
372                 i = errno;
373                 fclose (out);
374                 g_unlink (filename);
375                 errno = i;
376                 return -1;
377         }
378         
379         if (fclose (out) != 0) {
380                 i = errno;
381                 g_unlink (filename);
382                 errno = i;
383                 return -1;
384         }
385         
386         if (g_rename (filename, certdb->filename) == -1) {
387                 i = errno;
388                 g_unlink (filename);
389                 errno = i;
390                 return -1;
391         }
392         
393         certdb->flags &= ~CAMEL_CERTDB_DIRTY;
394         
395         return 0;
396         
397  error:
398         
399         g_warning ("Cannot save certificate database: %s", strerror (ferror (out)));
400         
401         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
402         
403         i = errno;
404         fclose (out);
405         g_unlink (filename);
406         errno = i;
407         
408         return -1;
409 }
410
411 void
412 camel_certdb_touch (CamelCertDB *certdb)
413 {
414         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
415         
416         certdb->flags |= CAMEL_CERTDB_DIRTY;
417 }
418
419 CamelCert *
420 camel_certdb_get_cert (CamelCertDB *certdb, const char *fingerprint)
421 {
422         CamelCert *cert;
423         
424         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
425         
426         CAMEL_CERTDB_LOCK (certdb, db_lock);
427         
428         cert = g_hash_table_lookup (certdb->cert_hash, fingerprint);
429         if (cert)
430                 camel_certdb_cert_ref (certdb, cert);
431         
432         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
433         
434         return cert;
435 }
436
437 void
438 camel_certdb_add (CamelCertDB *certdb, CamelCert *cert)
439 {
440         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
441         
442         CAMEL_CERTDB_LOCK (certdb, db_lock);
443         
444         if (g_hash_table_lookup (certdb->cert_hash, cert->fingerprint)) {
445                 CAMEL_CERTDB_UNLOCK (certdb, db_lock);
446                 return;
447         }
448         
449         camel_certdb_cert_ref (certdb, cert);
450         g_ptr_array_add (certdb->certs, cert);
451         g_hash_table_insert (certdb->cert_hash, cert->fingerprint, cert);
452         
453         certdb->flags |= CAMEL_CERTDB_DIRTY;
454         
455         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
456 }
457
458 void
459 camel_certdb_remove (CamelCertDB *certdb, CamelCert *cert)
460 {
461         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
462         
463         CAMEL_CERTDB_LOCK (certdb, db_lock);
464         
465         if (g_hash_table_lookup (certdb->cert_hash, cert->fingerprint)) {
466                 g_hash_table_remove (certdb->cert_hash, cert->fingerprint);
467                 g_ptr_array_remove (certdb->certs, cert);
468                 camel_certdb_cert_unref (certdb, cert);
469                 
470                 certdb->flags |= CAMEL_CERTDB_DIRTY;
471         }
472         
473         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
474 }
475
476 static CamelCert *
477 certdb_cert_new (CamelCertDB *certdb)
478 {
479         CamelCert *cert;
480         
481         if (certdb->cert_chunks)
482                 cert = e_memchunk_alloc0 (certdb->cert_chunks);
483         else
484                 cert = g_malloc0 (certdb->cert_size);
485         
486         cert->refcount = 1;
487         
488         return cert;
489 }
490
491 CamelCert *
492 camel_certdb_cert_new (CamelCertDB *certdb)
493 {
494         CamelCert *cert;
495         
496         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
497         
498         CAMEL_CERTDB_LOCK (certdb, alloc_lock);
499         
500         cert = CAMEL_CERTDB_GET_CLASS (certdb)->cert_new (certdb);
501         
502         CAMEL_CERTDB_UNLOCK (certdb, alloc_lock);
503         
504         return cert;
505 }
506
507 void
508 camel_certdb_cert_ref (CamelCertDB *certdb, CamelCert *cert)
509 {
510         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
511         g_return_if_fail (cert != NULL);
512         
513         CAMEL_CERTDB_LOCK (certdb, ref_lock);
514         cert->refcount++;
515         CAMEL_CERTDB_UNLOCK (certdb, ref_lock);
516 }
517
518 static void
519 certdb_cert_free (CamelCertDB *certdb, CamelCert *cert)
520 {
521         g_free (cert->issuer);
522         g_free (cert->subject);
523         g_free (cert->hostname);
524         g_free (cert->fingerprint);
525         if (cert->rawcert)
526                 g_byte_array_free(cert->rawcert, TRUE);
527 }
528
529 void
530 camel_certdb_cert_unref (CamelCertDB *certdb, CamelCert *cert)
531 {
532         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
533         g_return_if_fail (cert != NULL);
534         
535         CAMEL_CERTDB_LOCK (certdb, ref_lock);
536         
537         if (cert->refcount <= 1) {
538                 CAMEL_CERTDB_GET_CLASS (certdb)->cert_free (certdb, cert);
539                 if (certdb->cert_chunks)
540                         e_memchunk_free (certdb->cert_chunks, cert);
541                 else
542                         g_free (cert);
543         } else {
544                 cert->refcount--;
545         }
546         
547         CAMEL_CERTDB_UNLOCK (certdb, ref_lock);
548 }
549
550
551 static gboolean
552 cert_remove (gpointer key, gpointer value, gpointer user_data)
553 {
554         return TRUE;
555 }
556
557 void
558 camel_certdb_clear (CamelCertDB *certdb)
559 {
560         CamelCert *cert;
561         int i;
562         
563         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
564         
565         CAMEL_CERTDB_LOCK (certdb, db_lock);
566         
567         g_hash_table_foreach_remove (certdb->cert_hash, cert_remove, NULL);
568         for (i = 0; i < certdb->certs->len; i++) {
569                 cert = (CamelCert *) certdb->certs->pdata[i];
570                 camel_certdb_cert_unref (certdb, cert);
571         }
572         
573         certdb->saved_certs = 0;
574         g_ptr_array_set_size (certdb->certs, 0);
575         certdb->flags |= CAMEL_CERTDB_DIRTY;
576         
577         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
578 }
579
580
581 static const char *
582 cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
583 {
584         switch (string) {
585         case CAMEL_CERT_STRING_ISSUER:
586                 return cert->issuer;
587         case CAMEL_CERT_STRING_SUBJECT:
588                 return cert->subject;
589         case CAMEL_CERT_STRING_HOSTNAME:
590                 return cert->hostname;
591         case CAMEL_CERT_STRING_FINGERPRINT:
592                 return cert->fingerprint;
593         default:
594                 return NULL;
595         }
596 }
597
598
599 const char *
600 camel_cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
601 {
602         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
603         g_return_val_if_fail (cert != NULL, NULL);
604         
605         /* FIXME: do locking? */
606         
607         return CAMEL_CERTDB_GET_CLASS (certdb)->cert_get_string (certdb, cert, string);
608 }
609
610 static void
611 cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
612 {
613         switch (string) {
614         case CAMEL_CERT_STRING_ISSUER:
615                 g_free (cert->issuer);
616                 cert->issuer = g_strdup (value);
617                 break;
618         case CAMEL_CERT_STRING_SUBJECT:
619                 g_free (cert->subject);
620                 cert->subject = g_strdup (value);
621                 break;
622         case CAMEL_CERT_STRING_HOSTNAME:
623                 g_free (cert->hostname);
624                 cert->hostname = g_strdup (value);
625                 break;
626         case CAMEL_CERT_STRING_FINGERPRINT:
627                 g_free (cert->fingerprint);
628                 cert->fingerprint = g_strdup (value);
629                 break;
630         default:
631                 break;
632         }
633 }
634
635
636 void
637 camel_cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
638 {
639         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
640         g_return_if_fail (cert != NULL);
641         
642         /* FIXME: do locking? */
643         
644         CAMEL_CERTDB_GET_CLASS (certdb)->cert_set_string (certdb, cert, string, value);
645 }
646
647
648 CamelCertTrust
649 camel_cert_get_trust (CamelCertDB *certdb, CamelCert *cert)
650 {
651         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), CAMEL_CERT_TRUST_UNKNOWN);
652         g_return_val_if_fail (cert != NULL, CAMEL_CERT_TRUST_UNKNOWN);
653         
654         return cert->trust;
655 }
656
657
658 void
659 camel_cert_set_trust (CamelCertDB *certdb, CamelCert *cert, CamelCertTrust trust)
660 {
661         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
662         g_return_if_fail (cert != NULL);
663         
664         cert->trust = trust;
665 }