2009-02-15 [colin] 3.7.0cvs66
[claws.git] / src / privacy.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2009 Hiroyuki Yamamoto & the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #include <glib.h>
21 #include <glib/gi18n.h>
22
23 #include "privacy.h"
24 #include "procmime.h"
25
26 static GSList *systems = NULL;
27 static gchar *privacy_last_error = NULL;
28
29 void privacy_set_error(const gchar *format, ...)
30 {
31         va_list args;
32         gchar buf[BUFSIZ];
33
34         va_start(args, format);
35         g_vsnprintf(buf, BUFSIZ, format, args);
36         va_end(args);
37         g_free(privacy_last_error);
38         privacy_last_error = g_strdup(buf);
39 }
40
41 static gchar tmp_privacy_error[BUFSIZ];
42
43 void privacy_reset_error(void)
44 {
45         g_free(privacy_last_error);
46         privacy_last_error = NULL;
47 }
48
49 gboolean privacy_peek_error(void)
50 {
51         return (privacy_last_error != NULL);
52 }
53
54 const gchar *privacy_get_error (void)
55 {
56         if (privacy_last_error) {
57                 strncpy2(tmp_privacy_error, privacy_last_error, BUFSIZ-1);
58                 privacy_reset_error();
59                 return tmp_privacy_error;
60         } else {
61                 return _("Unknown error");
62         }
63 }
64
65 static PrivacySystem *privacy_data_get_system(PrivacyData *data)
66 {
67         /* Make sure the cached system is still registered */
68         if (data->system && g_slist_find(systems, data->system))
69                 return data->system;
70         else
71                 return NULL;
72 }
73 /**
74  * Register a new Privacy System
75  *
76  * \param system The Privacy System that should be registered
77  */
78 void privacy_register_system(PrivacySystem *system)
79 {
80         systems = g_slist_append(systems, system);
81 }
82
83 /**
84  * Unregister a new Privacy System. The system must not be in
85  * use anymore when it is unregistered.
86  *
87  * \param system The Privacy System that should be unregistered
88  */
89 void privacy_unregister_system(PrivacySystem *system)
90 {
91         systems = g_slist_remove(systems, system);
92 }
93
94 /**
95  * Free a PrivacyData of a PrivacySystem
96  *
97  * \param privacydata The data to free
98  */
99 void privacy_free_privacydata(PrivacyData *privacydata)
100 {
101         PrivacySystem *system = NULL;
102         
103         g_return_if_fail(privacydata != NULL);
104
105         system = privacy_data_get_system(privacydata);
106         if (!system)
107                 return;
108         system->free_privacydata(privacydata);
109 }
110
111 /**
112  * Check if a MimeInfo is signed with one of the available
113  * privacy system. If a privacydata is set in the MimeInfo
114  * it will directory return the return value by the system
115  * set in the privacy data or check all available privacy
116  * systems otherwise.
117  *
118  * \return True if the MimeInfo has a signature
119  */
120 gboolean privacy_mimeinfo_is_signed(MimeInfo *mimeinfo)
121 {
122         GSList *cur;
123         g_return_val_if_fail(mimeinfo != NULL, FALSE);
124
125         if (mimeinfo->privacy != NULL) {
126                 PrivacySystem *system = 
127                         privacy_data_get_system(mimeinfo->privacy);
128
129                 if (system == NULL) {
130                         mimeinfo->privacy = NULL;
131                         goto try_others;
132                 }
133
134                 if (system->is_signed != NULL)
135                         return system->is_signed(mimeinfo);
136                 else
137                         return FALSE;
138         }
139 try_others:
140         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
141                 PrivacySystem *system = (PrivacySystem *) cur->data;
142
143                 if(system->is_signed != NULL && system->is_signed(mimeinfo))
144                         return TRUE;
145         }
146
147         return FALSE;
148 }
149
150 struct SignedState {
151         MsgInfo *msginfo;
152         gchar **system;
153 };
154
155 static void msginfo_set_signed_flag(GNode *node, gpointer data)
156 {
157         struct SignedState *sstate = (struct SignedState *)data;
158         MsgInfo *msginfo = sstate->msginfo;
159         MimeInfo *mimeinfo = node->data;
160         
161         if (privacy_mimeinfo_is_signed(mimeinfo)) {
162                 procmsg_msginfo_set_flags(msginfo, 0, MSG_SIGNED);
163                 if (sstate->system && !*(sstate->system) && mimeinfo->privacy)
164                         *(sstate->system) = g_strdup(mimeinfo->privacy->system->id);
165         }
166         if (privacy_mimeinfo_is_encrypted(mimeinfo)) {
167                 procmsg_msginfo_set_flags(msginfo, 0, MSG_ENCRYPTED);
168                 if (sstate->system && !*(sstate->system) && mimeinfo->privacy)
169                         *(sstate->system) = g_strdup(mimeinfo->privacy->system->id);
170         } else {
171                 /* searching inside encrypted parts doesn't really make sense */
172                 g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_signed_flag, sstate);
173         }
174 }
175
176 void privacy_msginfo_get_signed_state(MsgInfo *msginfo, gchar **system)
177 {
178         struct SignedState sstate;
179         MimeInfo *mimeinfo = procmime_scan_message(msginfo);
180
181         sstate.msginfo = msginfo;
182         sstate.system = system;
183         g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_signed_flag, &sstate);
184 }
185
186 /**
187  * Check the signature of a MimeInfo. privacy_mimeinfo_is_signed
188  * should be called before otherwise it is done by this function.
189  * If the MimeInfo is not signed an error code will be returned.
190  *
191  * \return Error code indicating the result of the check,
192  *         < 0 if an error occured
193  */
194 gint privacy_mimeinfo_check_signature(MimeInfo *mimeinfo)
195 {
196         PrivacySystem *system;
197
198         g_return_val_if_fail(mimeinfo != NULL, -1);
199
200         if (mimeinfo->privacy == NULL)
201                 privacy_mimeinfo_is_signed(mimeinfo);
202         
203         if (mimeinfo->privacy == NULL)
204                 return -1;
205         
206         system = privacy_data_get_system(mimeinfo->privacy);
207         if (system == NULL)
208                 return -1;
209
210         if (system->check_signature == NULL)
211                 return -1;
212         
213         return system->check_signature(mimeinfo);
214 }
215
216 SignatureStatus privacy_mimeinfo_get_sig_status(MimeInfo *mimeinfo)
217 {
218         PrivacySystem *system;
219
220         g_return_val_if_fail(mimeinfo != NULL, -1);
221
222         if (mimeinfo->privacy == NULL)
223                 privacy_mimeinfo_is_signed(mimeinfo);
224         
225         if (mimeinfo->privacy == NULL)
226                 return SIGNATURE_UNCHECKED;
227         
228         system = privacy_data_get_system(mimeinfo->privacy);
229         if (system == NULL)
230                 return SIGNATURE_UNCHECKED;
231         if (system->get_sig_status == NULL)
232                 return SIGNATURE_UNCHECKED;
233         
234         return system->get_sig_status(mimeinfo);
235 }
236
237 gchar *privacy_mimeinfo_sig_info_short(MimeInfo *mimeinfo)
238 {
239         PrivacySystem *system;
240
241         g_return_val_if_fail(mimeinfo != NULL, NULL);
242
243         if (mimeinfo->privacy == NULL)
244                 privacy_mimeinfo_is_signed(mimeinfo);
245         
246         if (mimeinfo->privacy == NULL)
247                 return g_strdup(_("No signature found"));
248         
249         system = privacy_data_get_system(mimeinfo->privacy);
250         if (system == NULL)
251                 return g_strdup(_("No signature found"));
252         if (system->get_sig_info_short == NULL)
253                 return g_strdup(_("No information available"));
254         
255         return system->get_sig_info_short(mimeinfo);
256 }
257
258 gchar *privacy_mimeinfo_sig_info_full(MimeInfo *mimeinfo)
259 {
260         PrivacySystem *system;
261
262         g_return_val_if_fail(mimeinfo != NULL, NULL);
263
264         if (mimeinfo->privacy == NULL)
265                 privacy_mimeinfo_is_signed(mimeinfo);
266         
267         if (mimeinfo->privacy == NULL)
268                 return g_strdup(_("No signature found"));
269         
270         system = privacy_data_get_system(mimeinfo->privacy);
271         if (system == NULL)
272                 return g_strdup(_("No signature found"));
273         if (system->get_sig_info_full == NULL)
274                 return g_strdup(_("No information available"));
275         
276         return system->get_sig_info_full(mimeinfo);
277 }
278
279 gboolean privacy_mimeinfo_is_encrypted(MimeInfo *mimeinfo)
280 {
281         GSList *cur;
282         g_return_val_if_fail(mimeinfo != NULL, FALSE);
283
284         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
285                 PrivacySystem *system = (PrivacySystem *) cur->data;
286
287                 if(system->is_encrypted != NULL && system->is_encrypted(mimeinfo))
288                         return TRUE;
289         }
290
291         return FALSE;
292 }
293
294 static gint decrypt(MimeInfo *mimeinfo, PrivacySystem *system)
295 {
296         MimeInfo *decryptedinfo, *parentinfo;
297         gint childnumber;
298         
299         g_return_val_if_fail(system->decrypt != NULL, -1);
300         
301         decryptedinfo = system->decrypt(mimeinfo);
302         if (decryptedinfo == NULL)
303                 return -1;
304
305         parentinfo = procmime_mimeinfo_parent(mimeinfo);
306         childnumber = g_node_child_index(parentinfo->node, mimeinfo);
307         
308         procmime_mimeinfo_free_all(mimeinfo);
309
310         g_node_insert(parentinfo->node, childnumber, decryptedinfo->node);
311
312         return 0;
313 }
314
315 gint privacy_mimeinfo_decrypt(MimeInfo *mimeinfo)
316 {
317         GSList *cur;
318         g_return_val_if_fail(mimeinfo != NULL, FALSE);
319
320         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
321                 PrivacySystem *system = (PrivacySystem *) cur->data;
322
323                 if(system->is_encrypted != NULL && system->is_encrypted(mimeinfo))
324                         return decrypt(mimeinfo, system);
325         }
326
327         return -1;
328 }
329
330 GSList *privacy_get_system_ids()
331 {
332         GSList *cur;
333         GSList *ret = NULL;
334
335         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
336                 PrivacySystem *system = (PrivacySystem *) cur->data;
337
338                 ret = g_slist_append(ret, g_strdup(system->id));
339         }
340
341         return ret;
342 }
343
344 static PrivacySystem *privacy_get_system(const gchar *id)
345 {
346         GSList *cur;
347
348         g_return_val_if_fail(id != NULL, NULL);
349
350         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
351                 PrivacySystem *system = (PrivacySystem *) cur->data;
352
353                 if(strcmp(id, system->id) == 0)
354                         return system;
355         }
356
357         return NULL;
358 }
359
360 const gchar *privacy_system_get_name(const gchar *id)
361 {
362         PrivacySystem *system;
363
364         g_return_val_if_fail(id != NULL, NULL);
365
366         system = privacy_get_system(id);
367         if (system == NULL)
368                 return NULL;
369
370         return system->name;
371 }
372
373 gboolean privacy_system_can_sign(const gchar *id)
374 {
375         PrivacySystem *system;
376
377         g_return_val_if_fail(id != NULL, FALSE);
378
379         system = privacy_get_system(id);
380         if (system == NULL)
381                 return FALSE;
382
383         return system->can_sign;
384 }
385
386 gboolean privacy_system_can_encrypt(const gchar *id)
387 {
388         PrivacySystem *system;
389
390         g_return_val_if_fail(id != NULL, FALSE);
391
392         system = privacy_get_system(id);
393         if (system == NULL)
394                 return FALSE;
395
396         return system->can_encrypt;
397 }
398
399 gboolean privacy_sign(const gchar *id, MimeInfo *target, PrefsAccount *account, const gchar *from_addr)
400 {
401         PrivacySystem *system;
402
403         g_return_val_if_fail(id != NULL, FALSE);
404         g_return_val_if_fail(target != NULL, FALSE);
405
406         system = privacy_get_system(id);
407         if (system == NULL)
408                 return FALSE;
409         if (!system->can_sign)
410                 return FALSE;
411         if (system->sign == NULL)
412                 return FALSE;
413
414         return system->sign(target, account, from_addr);
415 }
416
417 gchar *privacy_get_encrypt_data(const gchar *id, GSList *recp_names)
418 {
419         PrivacySystem *system;
420         gchar *ret = NULL;
421         GSList *uniq_names = NULL, *cur;
422
423         g_return_val_if_fail(id != NULL, NULL);
424         g_return_val_if_fail(recp_names != NULL, NULL);
425
426         system = privacy_get_system(id);
427         if (system == NULL)
428                 return NULL;
429         if (!system->can_encrypt)
430                 return NULL;
431         if (system->get_encrypt_data == NULL)
432                 return NULL;
433
434         for (cur = recp_names; cur; cur = cur->next) {
435                 if (!g_slist_find_custom(uniq_names, cur->data, (GCompareFunc)strcmp)) {
436                         uniq_names = g_slist_prepend(uniq_names, cur->data);
437                 }
438         }
439         ret = system->get_encrypt_data(uniq_names);
440         
441         g_slist_free(uniq_names);
442         return ret;
443 }
444
445 const gchar *privacy_get_encrypt_warning(const gchar *id)
446 {
447         PrivacySystem *system;
448
449         g_return_val_if_fail(id != NULL, NULL);
450
451         system = privacy_get_system(id);
452         if (system == NULL)
453                 return NULL;
454         if (!system->can_encrypt)
455                 return NULL;
456         if (system->get_encrypt_warning == NULL)
457                 return NULL;
458
459         return system->get_encrypt_warning();
460 }
461
462 void privacy_inhibit_encrypt_warning(const gchar *id, gboolean inhibit)
463 {
464         PrivacySystem *system;
465
466         g_return_if_fail(id != NULL);
467
468         system = privacy_get_system(id);
469         if (system == NULL)
470                 return;
471         if (!system->can_encrypt)
472                 return;
473         if (system->inhibit_encrypt_warning == NULL)
474                 return;
475
476         system->inhibit_encrypt_warning(inhibit);
477 }
478
479 gboolean privacy_encrypt(const gchar *id, MimeInfo *mimeinfo, const gchar *encdata)
480 {
481         PrivacySystem *system;
482
483         g_return_val_if_fail(id != NULL, FALSE);
484         g_return_val_if_fail(mimeinfo != NULL, FALSE);
485         if (encdata == NULL) {
486                 privacy_set_error(_("No recipient keys defined."));
487                 return FALSE;
488         }
489
490         system = privacy_get_system(id);
491         if (system == NULL)
492                 return FALSE;
493         if (!system->can_encrypt)
494                 return FALSE;
495         if (system->encrypt == NULL)
496                 return FALSE;
497
498         return system->encrypt(mimeinfo, encdata);
499 }