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

Contents of /trunk/src/OECrypto.c

Parent Directory Parent Directory | Revision Log Revision Log


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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26