2011-10-17 [mones] 3.7.10cvs34
[claws.git] / src / privacy.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2011 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         cm_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         cm_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         if (!mimeinfo)
181                 return;
182         sstate.msginfo = msginfo;
183         sstate.system = system;
184         g_node_children_foreach(mimeinfo->node, G_TRAVERSE_ALL, msginfo_set_signed_flag, &sstate);
185 }
186
187 /**
188  * Check the signature of a MimeInfo. privacy_mimeinfo_is_signed
189  * should be called before otherwise it is done by this function.
190  * If the MimeInfo is not signed an error code will be returned.
191  *
192  * \return Error code indicating the result of the check,
193  *         < 0 if an error occured
194  */
195 gint privacy_mimeinfo_check_signature(MimeInfo *mimeinfo)
196 {
197         PrivacySystem *system;
198
199         cm_return_val_if_fail(mimeinfo != NULL, -1);
200
201         if (mimeinfo->privacy == NULL)
202                 privacy_mimeinfo_is_signed(mimeinfo);
203         
204         if (mimeinfo->privacy == NULL)
205                 return -1;
206         
207         system = privacy_data_get_system(mimeinfo->privacy);
208         if (system == NULL)
209                 return -1;
210
211         if (system->check_signature == NULL)
212                 return -1;
213         
214         return system->check_signature(mimeinfo);
215 }
216
217 SignatureStatus privacy_mimeinfo_get_sig_status(MimeInfo *mimeinfo)
218 {
219         PrivacySystem *system;
220
221         cm_return_val_if_fail(mimeinfo != NULL, -1);
222
223         if (mimeinfo->privacy == NULL)
224                 privacy_mimeinfo_is_signed(mimeinfo);
225         
226         if (mimeinfo->privacy == NULL)
227                 return SIGNATURE_UNCHECKED;
228         
229         system = privacy_data_get_system(mimeinfo->privacy);
230         if (system == NULL)
231                 return SIGNATURE_UNCHECKED;
232         if (system->get_sig_status == NULL)
233                 return SIGNATURE_UNCHECKED;
234         
235         return system->get_sig_status(mimeinfo);
236 }
237
238 gchar *privacy_mimeinfo_sig_info_short(MimeInfo *mimeinfo)
239 {
240         PrivacySystem *system;
241
242         cm_return_val_if_fail(mimeinfo != NULL, NULL);
243
244         if (mimeinfo->privacy == NULL)
245                 privacy_mimeinfo_is_signed(mimeinfo);
246         
247         if (mimeinfo->privacy == NULL)
248                 return g_strdup(_("No signature found"));
249         
250         system = privacy_data_get_system(mimeinfo->privacy);
251         if (system == NULL)
252                 return g_strdup(_("No signature found"));
253         if (system->get_sig_info_short == NULL)
254                 return g_strdup(_("No information available"));
255         
256         return system->get_sig_info_short(mimeinfo);
257 }
258
259 gchar *privacy_mimeinfo_sig_info_full(MimeInfo *mimeinfo)
260 {
261         PrivacySystem *system;
262
263         cm_return_val_if_fail(mimeinfo != NULL, NULL);
264
265         if (mimeinfo->privacy == NULL)
266                 privacy_mimeinfo_is_signed(mimeinfo);
267         
268         if (mimeinfo->privacy == NULL)
269                 return g_strdup(_("No signature found"));
270         
271         system = privacy_data_get_system(mimeinfo->privacy);
272         if (system == NULL)
273                 return g_strdup(_("No signature found"));
274         if (system->get_sig_info_full == NULL)
275                 return g_strdup(_("No information available"));
276         
277         return system->get_sig_info_full(mimeinfo);
278 }
279
280 gboolean privacy_mimeinfo_is_encrypted(MimeInfo *mimeinfo)
281 {
282         GSList *cur;
283         cm_return_val_if_fail(mimeinfo != NULL, FALSE);
284
285         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
286                 PrivacySystem *system = (PrivacySystem *) cur->data;
287
288                 if(system->is_encrypted != NULL && system->is_encrypted(mimeinfo))
289                         return TRUE;
290         }
291
292         return FALSE;
293 }
294
295 static gint decrypt(MimeInfo *mimeinfo, PrivacySystem *system)
296 {
297         MimeInfo *decryptedinfo, *parentinfo;
298         gint childnumber;
299         
300         cm_return_val_if_fail(system->decrypt != NULL, -1);
301         
302         decryptedinfo = system->decrypt(mimeinfo);
303         if (decryptedinfo == NULL)
304                 return -1;
305
306         parentinfo = procmime_mimeinfo_parent(mimeinfo);
307         childnumber = g_node_child_index(parentinfo->node, mimeinfo);
308         
309         procmime_mimeinfo_free_all(mimeinfo);
310
311         g_node_insert(parentinfo->node, childnumber, decryptedinfo->node);
312
313         return 0;
314 }
315
316 gint privacy_mimeinfo_decrypt(MimeInfo *mimeinfo)
317 {
318         GSList *cur;
319         cm_return_val_if_fail(mimeinfo != NULL, FALSE);
320
321         procmime_decode_content(mimeinfo);
322
323         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
324                 PrivacySystem *system = (PrivacySystem *) cur->data;
325
326                 if(system->is_encrypted != NULL && system->is_encrypted(mimeinfo))
327                         return decrypt(mimeinfo, system);
328         }
329
330         return -1;
331 }
332
333 GSList *privacy_get_system_ids()
334 {
335         GSList *cur;
336         GSList *ret = NULL;
337
338         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
339                 PrivacySystem *system = (PrivacySystem *) cur->data;
340
341                 ret = g_slist_append(ret, g_strdup(system->id));
342         }
343
344         return ret;
345 }
346
347 static PrivacySystem *privacy_get_system(const gchar *id)
348 {
349         GSList *cur;
350
351         cm_return_val_if_fail(id != NULL, NULL);
352
353         for(cur = systems; cur != NULL; cur = g_slist_next(cur)) {
354                 PrivacySystem *system = (PrivacySystem *) cur->data;
355
356                 if(strcmp(id, system->id) == 0)
357                         return system;
358         }
359
360         return NULL;
361 }
362
363 const gchar *privacy_system_get_name(const gchar *id)
364 {
365         PrivacySystem *system;
366
367         cm_return_val_if_fail(id != NULL, NULL);
368
369         system = privacy_get_system(id);
370         if (system == NULL)
371                 return NULL;
372
373         return system->name;
374 }
375
376 gboolean privacy_system_can_sign(const gchar *id)
377 {
378         PrivacySystem *system;
379
380         cm_return_val_if_fail(id != NULL, FALSE);
381
382         system = privacy_get_system(id);
383         if (system == NULL)
384                 return FALSE;
385
386         return system->can_sign;
387 }
388
389 gboolean privacy_system_can_encrypt(const gchar *id)
390 {
391         PrivacySystem *system;
392
393         cm_return_val_if_fail(id != NULL, FALSE);
394
395         system = privacy_get_system(id);
396         if (system == NULL)
397                 return FALSE;
398
399         return system->can_encrypt;
400 }
401
402 gboolean privacy_sign(const gchar *id, MimeInfo *target, PrefsAccount *account, const gchar *from_addr)
403 {
404         PrivacySystem *system;
405
406         cm_return_val_if_fail(id != NULL, FALSE);
407         cm_return_val_if_fail(target != NULL, FALSE);
408
409         system = privacy_get_system(id);
410         if (system == NULL)
411                 return FALSE;
412         if (!system->can_sign)
413                 return FALSE;
414         if (system->sign == NULL)
415                 return FALSE;
416
417         return system->sign(target, account, from_addr);
418 }
419
420 gchar *privacy_get_encrypt_data(const gchar *id, GSList *recp_names)
421 {
422         PrivacySystem *system;
423         gchar *ret = NULL;
424         GSList *uniq_names = NULL, *cur;
425
426         cm_return_val_if_fail(id != NULL, NULL);
427         cm_return_val_if_fail(recp_names != NULL, NULL);
428
429         system = privacy_get_system(id);
430         if (system == NULL)
431                 return NULL;
432         if (!system->can_encrypt)
433                 return NULL;
434         if (system->get_encrypt_data == NULL)
435                 return NULL;
436
437         for (cur = recp_names; cur; cur = cur->next) {
438                 if (!g_slist_find_custom(uniq_names, cur->data, (GCompareFunc)strcmp)) {
439                         uniq_names = g_slist_prepend(uniq_names, cur->data);
440                 }
441         }
442         ret = system->get_encrypt_data(uniq_names);
443         
444         g_slist_free(uniq_names);
445         return ret;
446 }
447
448 const gchar *privacy_get_encrypt_warning(const gchar *id)
449 {
450         PrivacySystem *system;
451
452         cm_return_val_if_fail(id != NULL, NULL);
453
454         system = privacy_get_system(id);
455         if (system == NULL)
456                 return NULL;
457         if (!system->can_encrypt)
458                 return NULL;
459         if (system->get_encrypt_warning == NULL)
460                 return NULL;
461
462         return system->get_encrypt_warning();
463 }
464
465 void privacy_inhibit_encrypt_warning(const gchar *id, gboolean inhibit)
466 {
467         PrivacySystem *system;
468
469         cm_return_if_fail(id != NULL);
470
471         system = privacy_get_system(id);
472         if (system == NULL)
473                 return;
474         if (!system->can_encrypt)
475                 return;
476         if (system->inhibit_encrypt_warning == NULL)
477                 return;
478
479         system->inhibit_encrypt_warning(inhibit);
480 }
481
482 gboolean privacy_encrypt(const gchar *id, MimeInfo *mimeinfo, const gchar *encdata)
483 {
484         PrivacySystem *system;
485
486         cm_return_val_if_fail(id != NULL, FALSE);
487         cm_return_val_if_fail(mimeinfo != NULL, FALSE);
488         if (encdata == NULL) {
489                 privacy_set_error(_("No recipient keys defined."));
490                 return FALSE;
491         }
492
493         system = privacy_get_system(id);
494         if (system == NULL)
495                 return FALSE;
496         if (!system->can_encrypt)
497                 return FALSE;
498         if (system->encrypt == NULL)
499                 return FALSE;
500
501         return system->encrypt(mimeinfo, encdata);
502 }