Bug 572977 – Use g_strerror() instead of strerror()
[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 (C) 1999-2008 Novell, Inc. (www.novell.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", g_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         /* no change, nothing new to save, simply return success */
341         if ((certdb->flags & CAMEL_CERTDB_DIRTY) == 0)
342                 return 0;
343
344         filename = alloca (strlen (certdb->filename) + 4);
345         sprintf (filename, "%s~", certdb->filename);
346         
347         fd = g_open (filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0600);
348         if (fd == -1)
349                 return -1;
350         
351         out = fdopen (fd, "wb");
352         if (out == NULL) {
353                 i = errno;
354                 close (fd);
355                 g_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                 g_unlink (filename);
379                 errno = i;
380                 return -1;
381         }
382         
383         if (fclose (out) != 0) {
384                 i = errno;
385                 g_unlink (filename);
386                 errno = i;
387                 return -1;
388         }
389         
390         if (g_rename (filename, certdb->filename) == -1) {
391                 i = errno;
392                 g_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", g_strerror (ferror (out)));
404         
405         CAMEL_CERTDB_UNLOCK (certdb, io_lock);
406         
407         i = errno;
408         fclose (out);
409         g_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         if (cert->rawcert)
530                 g_byte_array_free(cert->rawcert, TRUE);
531 }
532
533 void
534 camel_certdb_cert_unref (CamelCertDB *certdb, CamelCert *cert)
535 {
536         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
537         g_return_if_fail (cert != NULL);
538         
539         CAMEL_CERTDB_LOCK (certdb, ref_lock);
540         
541         if (cert->refcount <= 1) {
542                 CAMEL_CERTDB_GET_CLASS (certdb)->cert_free (certdb, cert);
543                 if (certdb->cert_chunks)
544                         e_memchunk_free (certdb->cert_chunks, cert);
545                 else
546                         g_free (cert);
547         } else {
548                 cert->refcount--;
549         }
550         
551         CAMEL_CERTDB_UNLOCK (certdb, ref_lock);
552 }
553
554
555 static gboolean
556 cert_remove (gpointer key, gpointer value, gpointer user_data)
557 {
558         return TRUE;
559 }
560
561 void
562 camel_certdb_clear (CamelCertDB *certdb)
563 {
564         CamelCert *cert;
565         int i;
566         
567         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
568         
569         CAMEL_CERTDB_LOCK (certdb, db_lock);
570         
571         g_hash_table_foreach_remove (certdb->cert_hash, cert_remove, NULL);
572         for (i = 0; i < certdb->certs->len; i++) {
573                 cert = (CamelCert *) certdb->certs->pdata[i];
574                 camel_certdb_cert_unref (certdb, cert);
575         }
576         
577         certdb->saved_certs = 0;
578         g_ptr_array_set_size (certdb->certs, 0);
579         certdb->flags |= CAMEL_CERTDB_DIRTY;
580         
581         CAMEL_CERTDB_UNLOCK (certdb, db_lock);
582 }
583
584
585 static const char *
586 cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
587 {
588         switch (string) {
589         case CAMEL_CERT_STRING_ISSUER:
590                 return cert->issuer;
591         case CAMEL_CERT_STRING_SUBJECT:
592                 return cert->subject;
593         case CAMEL_CERT_STRING_HOSTNAME:
594                 return cert->hostname;
595         case CAMEL_CERT_STRING_FINGERPRINT:
596                 return cert->fingerprint;
597         default:
598                 return NULL;
599         }
600 }
601
602
603 const char *
604 camel_cert_get_string (CamelCertDB *certdb, CamelCert *cert, int string)
605 {
606         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), NULL);
607         g_return_val_if_fail (cert != NULL, NULL);
608         
609         /* FIXME: do locking? */
610         
611         return CAMEL_CERTDB_GET_CLASS (certdb)->cert_get_string (certdb, cert, string);
612 }
613
614 static void
615 cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
616 {
617         switch (string) {
618         case CAMEL_CERT_STRING_ISSUER:
619                 g_free (cert->issuer);
620                 cert->issuer = g_strdup (value);
621                 break;
622         case CAMEL_CERT_STRING_SUBJECT:
623                 g_free (cert->subject);
624                 cert->subject = g_strdup (value);
625                 break;
626         case CAMEL_CERT_STRING_HOSTNAME:
627                 g_free (cert->hostname);
628                 cert->hostname = g_strdup (value);
629                 break;
630         case CAMEL_CERT_STRING_FINGERPRINT:
631                 g_free (cert->fingerprint);
632                 cert->fingerprint = g_strdup (value);
633                 break;
634         default:
635                 break;
636         }
637 }
638
639
640 void
641 camel_cert_set_string (CamelCertDB *certdb, CamelCert *cert, int string, const char *value)
642 {
643         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
644         g_return_if_fail (cert != NULL);
645         
646         /* FIXME: do locking? */
647         
648         CAMEL_CERTDB_GET_CLASS (certdb)->cert_set_string (certdb, cert, string, value);
649 }
650
651
652 CamelCertTrust
653 camel_cert_get_trust (CamelCertDB *certdb, CamelCert *cert)
654 {
655         g_return_val_if_fail (CAMEL_IS_CERTDB (certdb), CAMEL_CERT_TRUST_UNKNOWN);
656         g_return_val_if_fail (cert != NULL, CAMEL_CERT_TRUST_UNKNOWN);
657         
658         return cert->trust;
659 }
660
661
662 void
663 camel_cert_set_trust (CamelCertDB *certdb, CamelCert *cert, CamelCertTrust trust)
664 {
665         g_return_if_fail (CAMEL_IS_CERTDB (certdb));
666         g_return_if_fail (cert != NULL);
667         
668         cert->trust = trust;
669 }