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