Same as below. Also save errno before closing the file so our caller can
[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 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 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 General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <glib.h>
29 #include <stdio.h>
30 #include <string.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <pthread.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <errno.h>
37
38 #include "camel-certdb.h"
39 #include "camel-private.h"
40
41 #include <camel/camel-file-utils.h>
42
43 #include <e-util/e-memory.h>
44
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 #ifdef ENABLE_THREADS
121         certdb->priv->db_lock = g_mutex_new ();
122         certdb->priv->io_lock = g_mutex_new ();
123         certdb->priv->alloc_lock = g_mutex_new ();
124         certdb->priv->ref_lock = g_mutex_new ();
125 #endif
126 }
127
128 static void
129 camel_certdb_finalize (CamelObject *obj)
130 {
131         CamelCertDB *certdb = (CamelCertDB *) obj;
132         struct _CamelCertDBPrivate *p;
133         
134         p = certdb->priv;
135         
136         if (certdb->flags & CAMEL_CERTDB_DIRTY)
137                 camel_certdb_save (certdb);
138         
139         camel_certdb_clear (certdb);
140         g_ptr_array_free (certdb->certs, TRUE);
141         g_hash_table_destroy (certdb->cert_hash);
142         
143         g_free (certdb->filename);
144         
145         if (certdb->cert_chunks)
146                 e_memchunk_destroy (certdb->cert_chunks);
147         
148 #ifdef ENABLE_THREADS
149         g_mutex_free (p->db_lock);
150         g_mutex_free (p->io_lock);
151         g_mutex_free (p->alloc_lock);
152         g_mutex_free (p->ref_lock);
153 #endif
154         
155         g_free (p);
156 }
157
158
159 CamelCertDB *
160 camel_certdb_new (void)
161 {
162         return (CamelCertDB *) camel_object_new (camel_certdb_get_type ());
163 }
164
165
166 static CamelCertDB *default_certdb = NULL;
167 static pthread_mutex_t default_certdb_lock = PTHREAD_MUTEX_INITIALIZER;
168
169
170 void
171 camel_certdb_set_default (CamelCertDB *certdb)
172 {
173         pthread_mutex_lock (&default_certdb_lock);
174         
175         if (default_certdb)
176                 camel_object_unref (default_certdb);
177         
178         if (certdb)
179                 camel_object_ref (certdb);
180         
181         default_certdb = certdb;
182         
183         pthread_mutex_unlock (&default_certdb_lock);
184 }
185
186
187 CamelCertDB *
188 camel_certdb_get_default (void)
189 {
190         CamelCertDB *certdb;
191         
192         pthread_mutex_lock (&default_certdb_lock);
193         
194         if (default_certdb)
195                 camel_object_ref (default_certdb);
196         
197         certdb = default_certdb;
198         
199         pthread_mutex_unlock (&default_certdb_lock);
200         
201         return certdb;
202 }
203
204
205 void
206 camel_certdb_set_filename (CamelCertDB *certdb, const char *filename)
207 {
208         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
209         g_return_if_fail (filename != NULL);
210         
211         CAMEL_CERTDB_LOCK (certdb, db_lock);
212         
213         g_free (certdb->filename);
214         certdb->filename = g_strdup (filename);
215         
216         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
217 }
218
219
220 static int
221 certdb_header_load (CamelCertDB *certdb, FILE *istream)
222 {
223         if (camel_file_util_decode_uint32 (istream, &certdb->version) == -1)
224                 return -1;
225         if (camel_file_util_decode_uint32 (istream, &certdb->saved_certs) == -1)
226                 return -1;
227         
228         return 0;
229 }
230
231 static CamelCert *
232 certdb_cert_load (CamelCertDB *certdb, FILE *istream)
233 {
234         CamelCert *cert;
235         
236         cert = camel_certdb_cert_new (certdb);
237         
238         if (camel_file_util_decode_string (istream, &cert->issuer) == -1)
239                 goto error;
240         if (camel_file_util_decode_string (istream, &cert->subject) == -1)
241                 goto error;
242         if (camel_file_util_decode_string (istream, &cert->hostname) == -1)
243                 goto error;
244         if (camel_file_util_decode_string (istream, &cert->fingerprint) == -1)
245                 goto error;
246         if (camel_file_util_decode_uint32 (istream, &cert->trust) == -1)
247                 goto error;
248         
249         return cert;
250         
251  error:
252         
253         camel_certdb_cert_unref (certdb, cert);
254         
255         return NULL;
256 }
257
258 int
259 camel_certdb_load (CamelCertDB *certdb)
260 {
261         CamelCert *cert;
262         FILE *in;
263         int i;
264         
265         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
266         g_return_val_if_fail (certdb->filename, -1);
267         
268         in = fopen (certdb->filename, "r");
269         if (in == NULL)
270                 return -1;
271         
272         CAMEL_CERTDB_LOCK (certdb, io_lock);
273         if (CAMEL_CERTDB_GET_CLASS (certdb)->header_load (certdb, in) == -1)
274                 goto error;
275         
276         for (i = 0; i < certdb->saved_certs; i++) {
277                 cert = CAMEL_CERTDB_GET_CLASS (certdb)->cert_load (certdb, in);
278                 
279                 if (cert == NULL)
280                         goto error;
281                 
282                 camel_certdb_add (certdb, cert);
283         }
284         
285         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
286         
287         if (fclose (in) != 0)
288                 return -1;
289         
290         certdb->flags &= ~CAMEL_CERTDB_DIRTY;
291         
292         return 0;
293         
294  error:
295         
296         g_warning ("Cannot load certificate database: %s", strerror (ferror (in)));
297         
298         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
299         
300         fclose (in);
301         
302         return -1;
303 }
304
305 static int
306 certdb_header_save (CamelCertDB *certdb, FILE *ostream)
307 {
308         if (camel_file_util_encode_uint32 (ostream, certdb->version) == -1)
309                 return -1;
310         if (camel_file_util_encode_uint32 (ostream, certdb->saved_certs) == -1)
311                 return -1;
312         
313         return 0;
314 }
315
316 static int
317 certdb_cert_save (CamelCertDB *certdb, CamelCert *cert, FILE *ostream)
318 {
319         if (camel_file_util_encode_string (ostream, cert->issuer) == -1)
320                 return -1;
321         if (camel_file_util_encode_string (ostream, cert->subject) == -1)
322                 return -1;
323         if (camel_file_util_encode_string (ostream, cert->hostname) == -1)
324                 return -1;
325         if (camel_file_util_encode_string (ostream, cert->fingerprint) == -1)
326                 return -1;
327         if (camel_file_util_encode_uint32 (ostream, cert->trust) == -1)
328                 return -1;
329         
330         return 0;
331 }
332
333 int
334 camel_certdb_save (CamelCertDB *certdb)
335 {
336         CamelCert *cert;
337         char *filename;
338         int fd, i;
339         FILE *out;
340         
341         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), -1);
342         g_return_val_if_fail (certdb->filename, -1);
343         
344         filename = alloca (strlen (certdb->filename) + 4);
345         sprintf (filename, "%s~", certdb->filename);
346         
347         fd = open (filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
348         if (fd == -1)
349                 return -1;
350         
351         out = fdopen (fd, "w");
352         if (out == NULL) {
353                 i = errno;
354                 close (fd);
355                 unlink (filename);
356                 errno = i;
357                 return -1;
358         }
359         
360         CAMEL_CERTDB_LOCK (certdb, io_lock);
361         
362         certdb->saved_certs = certdb->certs->len;
363         if (CAMEL_CERTDB_GET_CLASS (certdb)->header_save (certdb, out) == -1)
364                 goto error;
365         
366         for (i = 0; i < certdb->saved_certs; i++) {
367                 cert = (CamelCert *) certdb->certs->pdata[i];
368                 
369                 if (CAMEL_CERTDB_GET_CLASS (certdb)->cert_save (certdb, cert, out) == -1)
370                         goto error;
371         }
372         
373         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
374         
375         if (fflush (out) != 0 || fsync (fileno (out)) == -1) {
376                 i = errno;
377                 fclose (out);
378                 unlink (filename);
379                 errno = i;
380                 return -1;
381         }
382         
383         if (fclose (out) != 0) {
384                 i = errno;
385                 unlink (filename);
386                 errno = i;
387                 return -1;
388         }
389         
390         if (rename (filename, certdb->filename) == -1) {
391                 i = errno;
392                 unlink (filename);
393                 errno = i;
394                 return -1;
395         }
396         
397         certdb->flags &= ~CAMEL_CERTDB_DIRTY;
398         
399         return 0;
400         
401  error:
402         
403         g_warning ("Cannot save certificate database: %s", strerror (ferror (out)));
404         
405         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
406         
407         i = errno;
408         fclose (out);
409         unlink (filename);
410         errno = i;
411         
412         return -1;
413 }
414
415 void
416 camel_certdb_touch (CamelCertDB *certdb)
417 {
418         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
419         
420         certdb->flags |= CAMEL_CERTDB_DIRTY;
421 }
422
423 CamelCert *
424 camel_certdb_get_cert (CamelCertDB *certdb, const char *fingerprint)
425 {
426         CamelCert *cert;
427         
428         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
429         
430         CAMEL_CERTDB_LOCK (certdb, db_lock);
431         
432         cert = g_hash_table_lookup (certdb->cert_hash, fingerprint);
433         if (cert)
434                 camel_certdb_cert_ref (certdb, cert);
435         
436         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
437         
438         return cert;
439 }
440
441 void
442 camel_certdb_add (CamelCertDB *certdb, CamelCert *cert)
443 {
444         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
445         
446         CAMEL_CERTDB_LOCK (certdb, db_lock);
447         
448         if (g_hash_table_lookup (certdb->cert_hash, cert->fingerprint)) {
449                 CAMEL_CERTDB_UNLOCK (certdb, db_lock);
450                 return;
451         }
452         
453         camel_certdb_cert_ref (certdb, cert);
454         g_ptr_array_add (certdb->certs, cert);
455         g_hash_table_insert (certdb->cert_hash, cert->fingerprint, cert);
456         
457         certdb->flags |= CAMEL_CERTDB_DIRTY;
458         
459         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
460 }
461
462 void
463 camel_certdb_remove (CamelCertDB *certdb, CamelCert *cert)
464 {
465         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
466         
467         CAMEL_CERTDB_LOCK (certdb, db_lock);
468         
469         if (g_hash_table_lookup (certdb->cert_hash, cert->fingerprint)) {
470                 g_hash_table_remove (certdb->cert_hash, cert->fingerprint);
471                 g_ptr_array_remove (certdb->certs, cert);
472                 camel_certdb_cert_unref (certdb, cert);
473                 
474                 certdb->flags |= CAMEL_CERTDB_DIRTY;
475         }
476         
477         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
478 }
479
480 static CamelCert *
481 certdb_cert_new (CamelCertDB *certdb)
482 {
483         CamelCert *cert;
484         
485         if (certdb->cert_chunks)
486                 cert = e_memchunk_alloc0 (certdb->cert_chunks);
487         else
488                 cert = g_malloc0 (certdb->cert_size);
489         
490         cert->refcount = 1;
491         
492         return cert;
493 }
494
495 CamelCert *
496 camel_certdb_cert_new (CamelCertDB *certdb)
497 {
498         CamelCert *cert;
499         
500         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
501         
502         CAMEL_CERTDB_LOCK (certdb, alloc_lock);
503         
504         cert = CAMEL_CERTDB_GET_CLASS (certdb)->cert_new (certdb);
505         
506         CAMEL_CERTDB_UNLOCK (certdb, alloc_lock);
507         
508         return cert;
509 }
510
511 void
512 camel_certdb_cert_ref (CamelCertDB *certdb, CamelCert *cert)
513 {
514         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
515         g_return_if_fail (cert != NULL);
516         
517         CAMEL_CERTDB_LOCK (certdb, ref_lock);
518         cert->refcount++;
519         CAMEL_CERTDB_UNLOCK (certdb, ref_lock);
520 }
521
522 static void
523 certdb_cert_free (CamelCertDB *certdb, CamelCert *cert)
524 {
525         g_free (cert->issuer);
526         g_free (cert->subject);
527         g_free (cert->hostname);
528         g_free (cert->fingerprint);
529 }
530
531 void
532 camel_certdb_cert_unref (CamelCertDB *certdb, CamelCert *cert)
533 {
534         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
535         g_return_if_fail (cert != NULL);
536         
537         CAMEL_CERTDB_LOCK (certdb, ref_lock);
538         
539         if (cert->refcount <= 1) {
540                 CAMEL_CERTDB_GET_CLASS (certdb)->cert_free (certdb, cert);
541                 if (certdb->cert_chunks)
542                         e_memchunk_free (certdb->cert_chunks, cert);
543                 else
544                         g_free (cert);
545         } else {
546                 cert->refcount--;
547         }
548         
549         CAMEL_CERTDB_UNLOCK (certdb, ref_lock);
550 }
551
552
553 static gboolean
554 cert_remove (gpointer key, gpointer value, gpointer user_data)
555 {
556         return TRUE;
557 }
558
559 void
560 camel_certdb_clear (CamelCertDB *certdb)
561 {
562         CamelCert *cert;
563         int i;
564         
565         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
566         
567         CAMEL_CERTDB_LOCK (certdb, db_lock);
568         
569         g_hash_table_foreach_remove (certdb->cert_hash, cert_remove, NULL);
570         for (i = 0; i < certdb->certs->len; i++) {
571                 cert = (CamelCert *) certdb->certs->pdata[i];
572                 camel_certdb_cert_unref (certdb, cert);
573         }
574         
575         certdb->saved_certs = 0;
576         g_ptr_array_set_size (certdb->certs, 0);
577         certdb->flags |= CAMEL_CERTDB_DIRTY;
578         
579         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
580 }
581
582
583 static const char *
584 cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
585 {
586         switch (string) {
587         case CAMEL_CERT_STRING_ISSUER:
588                 return cert->issuer;
589         case CAMEL_CERT_STRING_SUBJECT:
590                 return cert->subject;
591         case CAMEL_CERT_STRING_HOSTNAME:
592                 return cert->hostname;
593         case CAMEL_CERT_STRING_FINGERPRINT:
594                 return cert->fingerprint;
595         default:
596                 return NULL;
597         }
598 }
599
600
601 const char *
602 camel_cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
603 {
604         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
605         g_return_val_if_fail (cert != NULL, NULL);
606         
607         /* FIXME: do locking? */
608         
609         return CAMEL_CERTDB_GET_CLASS (certdb)->cert_get_string (certdb, cert, string);
610 }
611
612 static void
613 cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
614 {
615         switch (string) {
616         case CAMEL_CERT_STRING_ISSUER:
617                 g_free (cert->issuer);
618                 cert->issuer = g_strdup (value);
619                 break;
620         case CAMEL_CERT_STRING_SUBJECT:
621                 g_free (cert->subject);
622                 cert->subject = g_strdup (value);
623                 break;
624         case CAMEL_CERT_STRING_HOSTNAME:
625                 g_free (cert->hostname);
626                 cert->hostname = g_strdup (value);
627                 break;
628         case CAMEL_CERT_STRING_FINGERPRINT:
629                 g_free (cert->fingerprint);
630                 cert->fingerprint = g_strdup (value);
631                 break;
632         default:
633                 break;
634         }
635 }
636
637
638 void
639 camel_cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
640 {
641         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
642         g_return_if_fail (cert != NULL);
643         
644         /* FIXME: do locking? */
645         
646         CAMEL_CERTDB_GET_CLASS (certdb)->cert_set_string (certdb, cert, string, value);
647 }
648
649
650 CamelCertTrust
651 camel_cert_get_trust (CamelCertDB *certdb, CamelCert *cert)
652 {
653         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), CAMEL_CERT_TRUST_UNKNOWN);
654         g_return_val_if_fail (cert != NULL, CAMEL_CERT_TRUST_UNKNOWN);
655         
656         return cert->trust;
657 }
658
659
660 void
661 camel_cert_set_trust (CamelCertDB *certdb, CamelCert *cert, CamelCertTrust trust)
662 {
663         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
664         g_return_if_fail (cert != NULL);
665         
666         cert->trust = trust;
667 }