/[gpgoe]/trunk/src/OECrypto.c
ViewVC logotype

Contents of /trunk/src/OECrypto.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10 - (show annotations)
Mon Mar 27 12:47:50 2006 UTC (19 years, 1 month ago) by twoaday
File MIME type: text/plain
File size: 15417 byte(s)
cleanups.


1 /* OECrypto.c - OE crypto functions
2 * Copyright (C) 2001, 2002, 2003, 2006 Timo Schulz
3 *
4 * This file is part of GPGOE.
5 *
6 * GPGOE is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
10 *
11 * GPGOE is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with GPGOE; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <windows.h>
25 #include <assert.h>
26 #include "resource.h"
27 #include "gpgme.h"
28 #include "GPGOE.h"
29
30
31 /* Valid OpenPGP message types. */
32 enum {
33 PGP_MESSAGE = 1,
34 PGP_CLEARSIG = 2,
35 PGP_SIG = 4,
36 PGP_KEY = 8,
37 };
38
39
40 /* Move the keyboard focus to the message window
41 to copy the text to the clipboard. */
42 static void
43 set_focus (HWND main)
44 {
45 HWND doc, mime, msg;
46
47 doc = FindWindowEx (main, NULL, "ME_DocHost", NULL );
48 mime = FindWindowEx (doc, NULL, "##MimeEdit_Server", NULL);
49 msg = FindWindowEx (mime, NULL, "Internet Explorer_Server", NULL);
50
51 AttachThreadInput (GetCurrentThreadId (),
52 GetWindowThreadProcessId (main, NULL),
53 TRUE);
54
55 SetFocus (doc);
56 SetFocus (mime);
57 SetFocus (msg);
58
59 AttachThreadInput (GetCurrentThreadId (),
60 GetWindowThreadProcessId (main, NULL),
61 FALSE);
62 }
63
64
65 /* Copy the message to the clipboard and then return the clipboard
66 data (which is the message). */
67 static char*
68 window_get_message (HWND main_hwnd)
69 {
70 set_focus (main_hwnd);
71 SetForegroundWindow (main_hwnd);
72 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM(ID_OE5_SELECTALL, 0), 0);
73 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM(ID_OE5_COPY, 0), 0);
74
75 /* even so SendMessage() should wait, we wait for safety reasons. */
76 Sleep (200);
77 return get_clip_text (NULL);
78 }
79
80
81 /* Paste the message given in @msg into the window @main_hwnd.
82 To make sure all existing text is overwritten, "select all"
83 is send to the window first. */
84 static void
85 window_set_message (HWND main_hwnd, const char *msg)
86 {
87 set_clip_text (NULL, msg, strlen (msg));
88 set_focus (main_hwnd);
89 SetForegroundWindow (main_hwnd);
90 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE5_SELECTALL, 0), 0);
91 SendMessage (main_hwnd, WM_COMMAND, MAKEWPARAM (ID_OE5_PASTE, 0), 0);
92 }
93
94
95 /* Try to find the given recipient by key @key and
96 return it (NULL in case of error). */
97 static recip_list_t
98 find_recipient (recip_list_t rset, gpgme_key_t key)
99 {
100 recip_list_t n;
101 gpgme_user_id_t u;
102
103 for (n = rset; n; n = n->next) {
104 for (u = key->uids; u; u = u->next) {
105 if (u->email && strstr (u->email, n->addr))
106 return n;
107 }
108 }
109 return NULL;
110 }
111
112
113 /* Add recipient with address @addr and key @key to @rset. */
114 void
115 add_recipient (recip_list_t *rset, const char *addr, gpgme_key_t key)
116 {
117 recip_list_t r, n;
118
119 r = xcalloc (1, sizeof *r);
120 r->addr = xstrdup (addr);
121 r->key = key;
122 if (!*rset)
123 *rset = r;
124 else {
125 for (n=*rset; n->next; n=n->next)
126 ;
127 n->next = r;
128 }
129 }
130
131
132 /* Release recipient list @rset. */
133 static void
134 release_recipient (recip_list_t rset)
135 {
136 recip_list_t n;
137
138 while (rset != NULL) {
139 n = rset->next;
140 if (rset->key)
141 gpgme_key_release (rset->key);
142 free_if_alloc (rset->addr);
143 free_if_alloc (rset);
144 rset = n;
145 }
146 }
147
148
149 /* This functions converts the OE recipient fields to GPG compatible fields.
150 Currently the function support the following 'From' styles:
151 John Hacker <[email protected]>; [email protected]
152 or Foo Bar <[email protected]>, [email protected].
153 only the part between '<' '>' are added.
154 */
155 static int
156 parse_recipients (plugin_ctx_t ctx, recip_list_t *rset, char *recpt)
157 {
158 char *token, *p;
159 int count = 0;
160 char *p1, *p2;
161 int n1, n2;
162
163 assert (recpt && rset);
164
165 token = strtok (recpt, ",;");
166 while (token != NULL ) {
167 if ((p1=strchr (token, '<')) && (p2=strchr (token, '>'))
168 && strchr (token, '@')) {
169 n1 = p1 - token + 1 ;
170 n2 = p2 - token + 1;
171
172 p = xcalloc (1, strlen (token) + 1);
173 memcpy (p, token+n1, n2-n1-1);
174 add_recipient (rset, p, NULL);
175 count++;
176 free_if_alloc (p);
177 }
178 else if (strchr (token, '@') && !strchr (token, ' ')) {
179 p = xcalloc (1, strlen (token) + 1);
180 memcpy (p, token, strlen (token));
181 add_recipient (rset, p, NULL);
182 count++;
183 free_if_alloc (p);
184 }
185 token = strtok (NULL, ",;");
186 }
187 return count;
188 }
189
190
191 /* Fill in the key part of the recipient list @addrs if possible. */
192 static int
193 get_keys_from_addresses (recip_list_t addrs)
194 {
195 gpgme_ctx_t ctx;
196 gpgme_error_t err;
197 gpgme_key_t pk;
198 recip_list_t n, fnd;
199 char *p;
200 int nkeys=0;
201 DWORD len=0;
202
203 for (n = addrs; n; n = n->next)
204 len += strlen (n->addr) + 3;
205 p = xcalloc (1, len + 1);
206 for (n = addrs; n; n = n->next) {
207 strcat (p, n->addr);
208 strcat (p, " ");
209 }
210
211 err = gpgme_new (&ctx);
212 if (!err)
213 err = gpgme_op_keylist_start (ctx, p, 0);
214 while (!err) {
215 err = gpgme_op_keylist_next (ctx, &pk);
216 if (err)
217 break;
218 /* do not add invalid keys. */
219 if (pk->disabled || pk->expired || pk->revoked)
220 continue;
221 fnd = find_recipient (addrs, pk);
222 if (fnd != NULL && pk->can_encrypt) {
223 fnd->key = pk;
224 nkeys++;
225 }
226 }
227
228 gpgme_release (ctx);
229 free_if_alloc (p);
230 return nkeys;
231 }
232
233
234 /* Map gpgme data object to a string. */
235 void
236 map_gpgme_data (gpgme_data_t out, char **r_msg)
237 {
238 size_t len = 0;
239 char *p;
240
241 p = gpgme_data_release_and_get_mem (out, &len);
242 free_if_alloc (*r_msg);
243 *r_msg = xcalloc (1, len+1);
244 memcpy (*r_msg, p, len);
245 gpgme_free (p);
246 }
247
248
249 /* Try to extract the needed key information and retrieve
250 the keys if possible. */
251 static gpgme_error_t
252 get_keys (plugin_ctx_t ctx, recip_list_t *r_list,
253 gpgme_key_t **r_keys, int *r_n)
254 {
255 gpgme_key_t *keys;
256 recip_list_t addrs = NULL, n;
257 int rcount = 0, nkeys = 0;
258 int i;
259
260 if (ctx->to && strlen (ctx->to) >= 3)
261 rcount += parse_recipients (ctx, &addrs, ctx->to);
262 if (ctx->cc && strlen (ctx->cc) >= 3)
263 rcount += parse_recipients (ctx, &addrs, ctx->cc);
264 if (ctx->bcc && strlen (ctx->bcc) >= 3)
265 rcount += parse_recipients (ctx, &addrs, ctx->bcc);
266
267 nkeys = get_keys_from_addresses (addrs);
268 /* at least some keys were not found, so we offer to select them. */
269 if (nkeys != rcount) {
270 ctx->rset = addrs;
271 rcount = DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_ENCRYPT,
272 ctx->main_wnd, encrypt_dlg_proc, (LPARAM)ctx);
273 if (rcount == 0)
274 return gpg_error (GPG_ERR_NO_PUBKEY);
275 nkeys += rcount;
276 }
277
278 keys = xcalloc (nkeys+1, sizeof *keys);
279 for (n = addrs, i=0; n; n = n->next) {
280 if (n->key != NULL)
281 keys[i++] = n->key;
282 }
283 *r_list = addrs;
284 *r_keys = keys;
285 *r_n = nkeys;
286 return 0;
287 }
288
289
290 /* Encrypt the given message @r_msg with the recipients from the
291 To, Cc, Bcc fields. In case that a recipient was not found
292 use the recipient dialog so the user can select the missing keys. */
293 static gpgme_error_t
294 encrypt_msg (plugin_ctx_t ctx, char **r_msg)
295 {
296 gpgme_error_t err;
297 gpgme_key_t *keys = NULL;
298 gpgme_ctx_t gctx = NULL;
299 gpgme_data_t in = NULL, out = NULL;
300 recip_list_t list = NULL;
301 int nkeys=0;
302 char *msg = *r_msg;
303
304 assert (ctx);
305
306 err = get_keys (ctx, &list, &keys, &nkeys);
307 if (err)
308 return err;
309
310 err = gpgme_new (&gctx);
311 if (!err)
312 err = gpgme_data_new_from_mem (&in, msg, strlen (msg), 1);
313 if (!err)
314 err = gpgme_data_new (&out);
315 if (!err) {
316 gpgme_set_armor (gctx, 1);
317 gpgme_set_textmode (gctx, 1);
318 err = gpgme_op_encrypt (gctx, keys,
319 GPGME_ENCRYPT_ALWAYS_TRUST, in, out);
320 }
321
322 gpgme_release (gctx);
323 gpgme_data_release (in);
324 release_recipient (list);
325 free (keys);
326
327 if (err)
328 gpgme_data_release (out);
329 else
330 map_gpgme_data (out, r_msg);
331 return err;
332 }
333
334
335 /* Sign the message given in @r_msg with the GPG default key. */
336 static gpgme_error_t
337 sign_msg (plugin_ctx_t ctx, char **r_msg)
338 {
339 gpgme_error_t err;
340 gpgme_ctx_t gctx;
341 gpgme_data_t in, out;
342 pass_cb_t cb_val;
343
344 cb_val = new_pass_cb (ctx->main_wnd);
345
346 err = gpgme_new (&gctx);
347 if (!err)
348 err = gpgme_data_new_from_mem (&in, *r_msg, strlen (*r_msg), 1);
349 if (!err)
350 err = gpgme_data_new (&out);
351 if (!err) {
352 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
353 err = gpgme_op_sign (gctx, in, out, GPGME_SIG_MODE_CLEAR);
354 }
355
356 gpgme_release (gctx);
357 gpgme_data_release (in);
358 free_pass_cb (cb_val);
359
360 if (err)
361 gpgme_data_release (out);
362 else
363 map_gpgme_data (out, r_msg);
364
365 return err;
366 }
367
368
369 /* Sign and encrypt the message @r_msg. */
370 static gpgme_error_t
371 sign_encrypt_msg (plugin_ctx_t ctx, char **r_msg)
372 {
373 gpgme_ctx_t gctx;
374 gpgme_data_t in, out;
375 gpgme_error_t err;
376 gpgme_key_t *keys;
377 pass_cb_t cb_val;
378 recip_list_t list = NULL;
379 int ec, nkeys = 0;
380
381 ec = get_keys (ctx, &list, &keys, &nkeys);
382 if (ec)
383 return ec;
384
385 cb_val = new_pass_cb (ctx->main_wnd);
386
387 err = gpgme_new (&gctx);
388 if (!err)
389 err = gpgme_data_new_from_mem (&in, *r_msg, strlen (*r_msg), 1);
390 if (!err)
391 err = gpgme_data_new (&out);
392 if (!err) {
393 gpgme_set_armor (gctx, 1);
394 gpgme_set_textmode (gctx, 1);
395 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
396 err = gpgme_op_encrypt_sign (gctx, keys, GPGME_ENCRYPT_ALWAYS_TRUST,
397 in, out);
398 }
399
400 gpgme_release (gctx);
401 gpgme_data_release (in);
402 release_recipient (list);
403 free (keys);
404 free_pass_cb (cb_val);
405
406 if (err)
407 gpgme_data_release (out);
408 else
409 map_gpgme_data (out, r_msg);
410 return err;
411 }
412
413
414 static gpgme_error_t
415 verify_msg (plugin_ctx_t ctx, char **r_msg)
416 {
417 gpgme_error_t err;
418 gpgme_ctx_t gctx;
419 gpgme_data_t out = NULL;
420 gpgme_data_t sig = NULL;
421 gpgme_verify_result_t res;
422
423 err = gpgme_new (&gctx);
424 if (!err)
425 err = gpgme_data_new_from_mem (&sig, *r_msg, strlen (*r_msg), 1);
426 if (!err)
427 err = gpgme_data_new (&out);
428 if (!err)
429 err = gpgme_op_verify (gctx, sig, NULL, out);
430 if (!err) {
431 res = gpgme_op_verify_result (gctx);
432 if (res && res->signatures)
433 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VERIFY, ctx->main_wnd,
434 verify_dlg_proc, (LPARAM)res->signatures);
435 }
436
437 gpgme_release (gctx);
438 gpgme_data_release (sig);
439
440 if (!err)
441 map_gpgme_data (out, r_msg);
442 else
443 gpgme_data_release (out);
444 return err;
445 }
446
447
448 /* Detailed error message when the decryption failed. */
449 static void
450 store_decrypt_info (char *buf, size_t buflen,
451 gpgme_decrypt_result_t res)
452 {
453 gpgme_key_t key = NULL;
454 gpgme_ctx_t ctx;
455 int algo;
456
457 if (!res->recipients)
458 return;
459
460 if (!gpgme_new (&ctx)) {
461 gpgme_get_key (ctx, res->recipients->keyid, &key, 0);
462 gpgme_release (ctx);
463 }
464
465 algo = res->recipients->pubkey_algo;
466 if (!key)
467 _snprintf (buf, buflen, _("encrypted with %s key, ID %s\n"
468 "decryption failed: secret keyn not available"),
469 algo == 1? "RSA" : algo==16? "ELG": "???",
470 res->recipients->keyid+8);
471 else {
472 char *uid = utf8_to_native (key->uids->uid);
473 _snprintf (buf, buflen, _("encrypted with %d-bit %s key, ID %s\n"
474 "\t\"%s\"\n"
475 "decryption failed: secret keyn not available"),
476 key->subkeys->length,
477 algo == 1? "RSA" : algo==16? "ELG": "???",
478 key->subkeys->keyid+8, uid);
479 free_if_alloc (uid);
480 }
481 if (key)
482 gpgme_key_release (key);
483 }
484
485
486 /* Decrypt the message @r_msg. If the type @type is actually a signature,
487 the verify function is called instead of decryption. */
488 static gpgme_error_t
489 decrypt_msg (plugin_ctx_t ctx, char **r_msg, int type)
490 {
491 gpgme_ctx_t gctx = NULL;
492 gpgme_data_t in = NULL, out = NULL;
493 gpgme_error_t err;
494 gpgme_verify_result_t res;
495 pass_cb_t cb_val;
496 char *msg = *r_msg;
497
498 if ((type & PGP_SIG) || (type & PGP_CLEARSIG))
499 return verify_msg (ctx, r_msg);
500
501 cb_val = new_pass_cb (ctx->main_wnd);
502
503 err = gpgme_new (&gctx);
504 if (!err)
505 err = gpgme_data_new_from_mem (&in, msg, strlen (msg), 1);
506 if (!err)
507 err = gpgme_data_new (&out);
508 if (!err) {
509 gpgme_set_passphrase_cb (gctx, passphrase_cb, cb_val);
510 err = gpgme_op_decrypt_verify (gctx, in, out);
511 res = gpgme_op_verify_result (gctx);
512 if (res && res->signatures)
513 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VERIFY, ctx->main_wnd,
514 verify_dlg_proc, (LPARAM)res->signatures);
515 }
516 if (err) {
517 gpgme_decrypt_result_t r = gpgme_op_decrypt_result (gctx);
518 store_decrypt_info (ctx->errbuf, sizeof (ctx->errbuf)-1, r);
519 }
520
521 gpgme_release (gctx);
522 gpgme_data_release (in);
523 free_pass_cb (cb_val);
524
525 if (err)
526 gpgme_data_release (out);
527 else
528 map_gpgme_data (out, r_msg);
529
530 return err;
531 }
532
533
534 /* Return what kind of OpenPGP message @msg is. */
535 static int
536 parse_pgp_id (const char *msg)
537 {
538 int id = 0;
539
540 if (strstr (msg, "BEGIN PGP MESSAGE"))
541 id |= PGP_MESSAGE;
542 if (strstr (msg, "BEGIN PGP PUBLIC KEY") ||
543 strstr (msg, "BEGIN PGP SECRET KEY") ||
544 strstr (msg, "BEGIN PGP PRIVATE KEY"))
545 id |= PGP_KEY;
546 if (strstr (msg, "BEGIN PGP SIGNED MESSAGE"))
547 id |= PGP_CLEARSIG;
548 return id;
549 }
550
551
552 /* This function can be use for all kind of OE messages.
553 It automatically choose the right procedure to handle the data. */
554 gpgme_error_t
555 oe_handle_mail (plugin_ctx_t ctx)
556 {
557 gpgme_error_t rc = 0;
558 char *msg;
559 int msg_type = 0;
560
561 assert (ctx);
562
563 msg = window_get_message (ctx->main_wnd);
564 if (!msg || strlen (msg) == 2) {
565 free_if_alloc (msg);
566 return 0;
567 }
568
569 if (strstr (msg, "-----BEGIN PGP") &&
570 strstr (msg, "-----END PGP"))
571 msg_type = parse_pgp_id (msg);
572
573 if (msg_type & PGP_KEY) {
574 MessageBox (ctx->main_wnd,
575 _("This mail contains one or more public or secret keys.\n\n"
576 "Please save the mail text in a file to use WinPT to import them."),
577 _("GPG Plug-in Info"), MB_ICONINFORMATION|MB_OK);
578 }
579 else if (msg_type) {
580 rc = decrypt_msg (ctx, &msg, msg_type);
581 SendMessage (ctx->msg_wnd, WM_CLEAR, 0, 0);
582 SendMessage (ctx->msg_wnd, WM_UNDO, 0, 0);
583 if (!rc && (msg_type & PGP_MESSAGE) && msg && strlen (msg) > 0) {
584 struct viewer_ctx_s viewer;
585 viewer.msg = msg;
586 DialogBoxParam (mod_hinst_dll, (LPCTSTR)IDD_VIEWER, ctx->main_wnd,
587 viewer_dlg_proc, (LPARAM)&viewer);
588 }
589 }
590 else {
591 if (!ctx->to && !ctx->cc && !ctx->bcc) {
592 free_if_alloc (msg);
593 return gpg_error (GPG_ERR_NO_DATA);
594 }
595 if (ctx->sign && ctx->encrypt)
596 rc = sign_encrypt_msg (ctx, &msg);
597 else if (ctx->sign)
598 rc = sign_msg (ctx, &msg);
599 else if (ctx->encrypt)
600 rc = encrypt_msg (ctx, &msg);
601 window_set_message (ctx->main_wnd, msg);
602 }
603
604 free_if_alloc (msg);
605 return rc;
606 }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26