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

Contents of /trunk/src/OECrypto.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 16 - (show annotations)
Tue Apr 11 06:56:23 2006 UTC (19 years, 1 month ago) by twoaday
File MIME type: text/plain
File size: 17737 byte(s)


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26