/[winpt]/trunk/MyGPGME/decrypt.c
ViewVC logotype

Contents of /trunk/MyGPGME/decrypt.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 9 - (show annotations)
Wed Apr 13 11:10:53 2005 UTC (19 years, 10 months ago) by twoaday
File MIME type: text/plain
File size: 19174 byte(s)
Fix signature ownertrust code.


1 /* decrypt.c - decrypt functions
2 * Copyright (C) 2000 Werner Koch (dd9jn)
3 * Copyright (C) 2001-2005 Timo Schulz
4 *
5 * This file is part of MyGPGME.
6 *
7 * MyGPGME is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * MyGPGME is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
20 */
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <windows.h>
27
28 #include "util.h"
29 #include "context.h"
30 #include "ops.h"
31
32 struct decrypt_result_s {
33 gpgme_sig_t sig;
34 int no_data;
35 int no_seckey;
36 int no_passphrase;
37 int idea_cipher;
38 int okay;
39 int sig_okay;
40 int failed;
41 int bad_mdc;
42 int bad_armor;
43 int bad_passphrase;
44 int key_algo;
45 int file_start;
46 int file_done;
47 char keyid[16+1];
48 char cardno[32+1];
49 void * last_pw_handle;
50 char * userid_hint;
51 char * passphrase_info;
52 char * file_name;
53 };
54
55
56 void
57 gpgme_decrypt_get_sig_ctx (gpgme_ctx_t ctx, gpgme_sig_t * r_sig)
58 {
59 if (!ctx)
60 return;
61 if (r_sig)
62 *r_sig = NULL;
63 if (ctx && ctx->result.decrypt->sig) {
64 _gpgme_sig_ref (ctx->result.decrypt->sig);
65 if (r_sig)
66 *r_sig = ctx->result.decrypt->sig;
67 }
68 } /* gpgme_decrypt_get_sig_ctx */
69
70
71 void
72 gpgme_decrypt_get_status( gpgme_ctx_t ctx, char * keyid,
73 gpgme_op_flags_t * r_flags )
74 {
75 gpgme_op_flags_t flags = 0;
76 _decrypt_result_t res;
77
78 if( !keyid )
79 return;
80 if( ctx->result_type != RESULT_TYPE_DECRYPT )
81 return;
82 if( !ctx->result.decrypt->okay
83 && ctx->result.decrypt->no_seckey ) {
84 strcpy( keyid, ctx->result.decrypt->keyid );
85 flags |= GPGME_OPFLAG_NOSECKEY;
86 }
87 res = ctx->result.decrypt;
88 if (res->bad_armor)
89 flags |= GPGME_OPFLAG_BADARMOR;
90 if (res->bad_mdc)
91 flags |= GPGME_OPFLAG_BADMDC;
92 if (r_flags)
93 *r_flags = flags;
94 } /* gpgme_decrypt_get_status */
95
96
97 void
98 _gpgme_release_decrypt_result( _decrypt_result_t res )
99 {
100 if( res ) {
101 safe_free( res->passphrase_info );
102 safe_free( res->userid_hint );
103 gpgme_sig_release( res->sig );
104 safe_free( res );
105 }
106 } /* _gpgme_release_decrypt_result */
107
108
109 static gpgme_error_t
110 create_result_struct (gpgme_ctx_t ctx)
111 {
112 assert( !ctx->result.decrypt );
113 ctx->result.decrypt = calloc( 1, sizeof * ctx->result.decrypt );
114 if( !ctx->result.decrypt )
115 return mk_error( Out_Of_Core );
116 ctx->result_type = RESULT_TYPE_DECRYPT;
117 return 0;
118 } /* create_result_struct */
119
120
121 static void
122 list_status_handler( gpgme_ctx_t ctx, gpg_status_code_t code, char * args )
123 {
124 char keyid[32] = {0};
125
126 if (ctx->out_of_core)
127 return;
128 if (code == STATUS_ENC_TO) {
129 keyid[0] = (char)atol (args+17);
130 strncpy (keyid+1, args, 16);
131 gpgme_recipients_add_name (ctx->enc_to, keyid);
132 }
133 } /* list_status_handler */
134
135
136 static gpgme_sig_t
137 add_signature( gpgme_ctx_t ctx )
138 {
139 gpgme_sig_t sig, s;
140 gpgme_error_t err;
141
142 err = gpgme_sig_new( &sig );
143 if( err ) {
144 ctx->out_of_core = 1;
145 return NULL;
146 }
147 if( ctx->result.decrypt->file_name ) {
148 sig->file_name = strdup( ctx->result.decrypt->file_name );
149 if( !sig->file_name ) {
150 ctx->out_of_core = 1;
151 safe_free( sig );
152 return NULL;
153 }
154 }
155 if( !ctx->result.decrypt->sig )
156 ctx->result.decrypt->sig = sig;
157 else {
158 for( s = ctx->result.decrypt->sig; s->next; s=s->next )
159 ;
160 s->next = sig;
161 }
162 return sig;
163 } /* add_signature */
164
165
166 static void
167 decrypt_status_handler (gpgme_ctx_t ctx, gpg_status_code_t code, char *args)
168 {
169 static gpgme_sig_t sig = NULL;
170 static char keyid[16+1];
171 char *p = NULL, fpr[40+1];
172 int i = 0, j=0;
173 char ch = 0;
174
175 if (ctx->out_of_core)
176 return;
177
178 if (ctx->result_type == RESULT_TYPE_NONE) {
179 if (create_result_struct (ctx)) {
180 ctx->out_of_core = 1;
181 return;
182 }
183 }
184
185 assert (ctx->result_type == RESULT_TYPE_DECRYPT);
186
187 if (code == STATUS_GOODSIG || code == STATUS_REVKEYSIG
188 || code == STATUS_EXPKEYSIG || code == STATUS_BADSIG
189 || code == STATUS_ERRSIG) {
190 sig = add_signature (ctx);
191 if (!sig || ctx->out_of_core)
192 return;
193 }
194
195 _gpgme_nodata_status_handler (code, args, &ctx->result.decrypt->no_data);
196 _gpgme_pass_status_handler (code, args, &ctx->result.decrypt->bad_passphrase,
197 &ctx->result.decrypt->no_data,
198 &ctx->result.decrypt->passphrase_info);
199 if (sig && (code >= STATUS_TRUST_UNDEFINED && code <= STATUS_TRUST_ULTIMATE)) {
200 _gpgme_sigtrust_status_handler (code, args, &sig->trust);
201 DEBUG1("sig_trust: %d", sig->trust);
202 }
203
204 switch (code) {
205 case STATUS_EOF:
206 break;
207
208 case STATUS_USERID_HINT:
209 safe_free (ctx->result.decrypt->userid_hint);
210 p = ctx->result.decrypt->userid_hint = strdup (args);
211 if (!p) {
212 ctx->out_of_core = 1;
213 return;
214 }
215 break;
216
217 case STATUS_DECRYPTION_OKAY:
218 ctx->result.decrypt->okay = 1;
219 break;
220
221 case STATUS_DECRYPTION_FAILED:
222 ctx->result.decrypt->failed = 1;
223 break;
224
225 case STATUS_RSA_OR_IDEA:
226 ctx->result.decrypt->idea_cipher = 1;
227 break;
228
229 case STATUS_SIG_ID:
230 DEBUG0 ("Plaintext was signed!\n");
231 break;
232
233 case STATUS_NO_SECKEY:
234 ctx->result.decrypt->no_seckey++;
235 strncpy (ctx->result.decrypt->keyid, args, 16);
236 break;
237
238 case STATUS_NO_PUBKEY:
239 sig->sigstat = GPGME_SIG_STAT_NOKEY;
240 break;
241
242 case STATUS_VALIDSIG:
243 p = fpr;
244 for( i = 0; i < DIM(fpr) && args[i] && args[i] != ' ' ; i++ )
245 *p++ = args[i];
246 *p = 0;
247 /* skip the formatted date */
248 while ( args[i] && args[i] == ' ')
249 i++;
250 while ( args[i] && args[i] != ' ')
251 i++;
252 /* and get the timestamp */
253 sig->created = strtoul( args + i, NULL, 10 );
254 ctx->result.decrypt->sig_okay = 1;
255 break;
256
257 case STATUS_GOODSIG:
258 case STATUS_BADSIG:
259 if (code == STATUS_GOODSIG)
260 sig->sigstat = GPGME_SIG_STAT_GOOD;
261 else
262 sig->sigstat = GPGME_SIG_STAT_BAD;
263 strncpy( sig->id, args, 16 );
264 sig->user_id = p = calloc (1, strlen (args + 16) + 2);
265 if (!p)
266 {
267 ctx->out_of_core = 1;
268 return;
269 }
270 strcpy (p, args + 16);
271 break;
272
273 case STATUS_EXPKEYSIG:
274 case STATUS_REVKEYSIG:
275 if( code == STATUS_EXPKEYSIG )
276 sig->sigstat = GPGME_SIG_STAT_E_GOOD;
277 else if( code == STATUS_REVKEYSIG )
278 sig->sigstat = GPGME_SIG_STAT_R_GOOD;
279 for( i=0, p=sig->id; args[i] && args[i] != ' '; i++ )
280 *p++ = args[i];
281 *p = '\0';
282 p = sig->user_id = calloc( 1, strlen( args+i ) + 2 );
283 if( !p ) {
284 ctx->out_of_core = 1;
285 return;
286 }
287 j=0;
288 while (args[i])
289 p[j++] = args[i++];
290 p[j++] = '\0';
291 break;
292
293 case STATUS_ERRSIG:
294 sig->sigstat = GPGME_SIG_STAT_ERROR;
295 break;
296
297 case STATUS_BADMDC:
298 ctx->result.decrypt->bad_mdc = 1;
299 break;
300
301 case STATUS_BADARMOR:
302 ctx->result.decrypt->bad_armor = 1;
303 break;
304
305 case STATUS_FILE_START:
306 if (*args == '3') {
307 safe_free (ctx->result.decrypt->file_name);
308 p = ctx->result.decrypt->file_name = strdup (args+2);
309 if( !p ) {
310 ctx->out_of_core = 1;
311 return;
312 }
313 }
314 ctx->result.decrypt->file_start++;
315 if (ctx->cb.interactiv)
316 ctx->cb.interactiv (ctx->cb.interactiv_value, code, NULL, args+2);
317 break;
318
319 case STATUS_FILE_DONE:
320 ctx->result.decrypt->file_done++;
321 if( ctx->cb.interactiv )
322 ctx->cb.interactiv( ctx->cb.interactiv_value, code, NULL, NULL );
323 break;
324
325 case STATUS_CARDCTRL:
326 ch = args[i++];
327 if (ch == '4') {
328 ctx->result.decrypt->no_seckey = -1;
329 break;
330 }
331 if (ch != '3')
332 break;
333 i++;
334 p = ctx->result.decrypt->cardno;
335 for (; i-1 < DIM (ctx->result.decrypt->cardno) && args[i]; i++)
336 *p++ = args[i];
337 *p = 0;
338 break;
339
340 default:
341 break; /* ignore all other codes */
342 }
343 } /* decrypt_status_handler */
344
345
346 static const char *
347 decrypt_command_handler (void * opaque, gpg_status_code_t code, const char * key)
348 {
349 gpgme_ctx_t c = opaque;
350
351 if (!code) {
352 /* We have been called for cleanup */
353 if (c->cb.passphrase) {
354 c->cb.passphrase (c->cb.passphrase_value, NULL,
355 &c->result.decrypt->last_pw_handle);
356 }
357 return NULL;
358 }
359
360 if (!key || !c->cb.passphrase)
361 return NULL;
362
363 if (c->result_type == RESULT_TYPE_NONE) {
364 if (create_result_struct (c)) {
365 c->out_of_core = 1;
366 return NULL;
367 }
368 }
369
370 if( code == STATUS_GET_HIDDEN
371 && (!strcmp( key, "passphrase.enter" )
372 || !strcmp( key, "passphrase.pin.ask" )) ) {
373 const char * userid_hint = c->result.decrypt->userid_hint;
374 const char * passphrase_info = c->result.decrypt->passphrase_info;
375 const char * cardno = c->result.decrypt->cardno;
376 int bad_passphrase = c->result.decrypt->bad_passphrase;
377 int is_card=0;
378 char * buf;
379 const char *s;
380
381 c->result.decrypt->bad_passphrase = 0;
382 is_card = !strcmp (key, "passphrase.pin.ask");
383 if (!userid_hint)
384 userid_hint = "[User ID hint missing]";
385 if (!passphrase_info)
386 passphrase_info = "[passphrase info missing]";
387 buf = malloc (20 + strlen (userid_hint)
388 + strlen (passphrase_info) + 3);
389 if( !buf ) {
390 c->out_of_core = 1;
391 return NULL;
392 }
393 sprintf (buf, "%s\n%s\n%s", bad_passphrase? "TRY_AGAIN":"ENTER_PASSPHRASE",
394 userid_hint, passphrase_info);
395 s = c->cb.passphrase (c->cb.passphrase_value, is_card? cardno : buf,
396 &c->result.decrypt->last_pw_handle);
397 safe_free (buf);
398 return s;
399 }
400 else if( (code == STATUS_GET_BOOL
401 && !strcmp( key, "openfile.overwrite.okay" ))
402 || (code == STATUS_GET_LINE && !strcmp( key, "openfile.askoutname" )) )
403 if (c->cb.interactiv)
404 return c->cb.interactiv( c->cb.interactiv_value, code, key, NULL );
405
406 return NULL;
407 } /* decrypt_command_handler */
408
409
410 static gpgme_error_t
411 list_keys_start( gpgme_ctx_t ctx, gpgme_data_t ciph, const char * file,
412 gpgme_recipients_t * r_keys )
413 {
414 gpgme_error_t rc;
415 gpgme_recipients_t keys;
416 FILE * fp;
417 const char * s;
418 char * p;
419
420 if (!r_keys)
421 return mk_error (Invalid_Value);
422 if (ciph && file || !ciph && !file)
423 return mk_error (Invalid_Mode);
424
425 *r_keys = NULL;
426 fail_on_pending_request( ctx );
427 ctx->pending = 1;
428
429 _gpgme_gpg_release( &ctx->gpg );
430 rc = _gpgme_gpg_new( &ctx->gpg );
431 if( rc )
432 return rc;
433
434 if( ciph ) {
435 p = _gpgme_data_get_as_string( ciph );
436 if( !p )
437 return mk_error( Out_Of_Core );
438
439 s = _gpgme_get_tmpfile( 0 );
440 fp = fopen( s, "wb" );
441 if( !fp ) {
442 safe_free( p );
443 return mk_error( File_Error );
444 }
445 fwrite (p, 1, strlen (p), fp);
446 fclose (fp);
447 safe_free (p);
448 }
449 else
450 s = file;
451
452 rc = gpgme_recipients_new( &keys );
453 if( rc )
454 return rc;
455 ctx->enc_to = keys;
456
457 _gpgme_gpg_set_status_handler( ctx->gpg, list_status_handler, ctx );
458 _gpgme_gpg_add_arg( ctx->gpg, "--list-only" );
459 _gpgme_gpg_add_arg( ctx->gpg, s );
460
461 rc = _gpgme_gpg_spawn( ctx->gpg, ctx );
462 if( rc ) {
463 ctx->pending = 0;
464 _gpgme_gpg_release( &ctx->gpg );
465 gpgme_recipients_release( keys ); keys = NULL;
466 }
467 *r_keys = keys;
468 return rc;
469 } /* list_keys_start */
470
471
472 /* It either works with a file or a data object but not with both! */
473 gpgme_error_t
474 gpgme_op_list_keys( gpgme_data_t ciph, const char * file,
475 gpgme_recipients_t * r_rset )
476 {
477 gpgme_ctx_t ctx = NULL;
478 gpgme_error_t err;
479
480 if( !r_rset )
481 return mk_error (Invalid_Value);
482 *r_rset = NULL;
483 err = gpgme_new( &ctx );
484 if( !err )
485 err = list_keys_start( ctx, ciph, file, r_rset );
486 if( !err ) {
487 gpgme_wait( ctx, 1 );
488 ctx->pending = 0;
489 }
490 gpgme_release( ctx );
491 return err;
492 } /* gpgme_op_list_keys */
493
494
495 static gpgme_error_t
496 file_decrypt_start( gpgme_ctx_t ctx, const char ** input, size_t nfiles,
497 const char * output )
498 {
499 gpgme_error_t rc;
500
501 if( !input )
502 return mk_error( Invalid_Value );
503
504 fail_on_pending_request( ctx );
505 ctx->pending = 1;
506
507 _gpgme_gpg_release( &ctx->gpg );
508 rc = _gpgme_gpg_new( &ctx->gpg );
509 if( rc )
510 return rc;
511
512 _gpgme_gpg_set_status_handler( ctx->gpg, decrypt_status_handler, ctx );
513 if( !ctx->use_pass_fd )
514 _gpgme_gpg_set_command_handler( ctx->gpg, decrypt_command_handler, ctx );
515 else {
516 rc = _gpgme_add_passphrase( ctx );
517 if( rc ) {
518 _gpgme_gpg_release( &ctx->gpg );
519 return rc;
520 }
521 }
522
523 if( !ctx->cb.interactiv )
524 _gpgme_gpg_add_arg( ctx->gpg, "--yes" );
525 if( nfiles > 1 || !output )
526 _gpgme_gpg_add_arg( ctx->gpg, "--no-mangle-dos-filenames" );
527 if( ctx->pipemode || nfiles > 1 )
528 _gpgme_gpg_add_arg( ctx->gpg, "--decrypt-files" );
529 else
530 _gpgme_gpg_add_arg( ctx->gpg, "--decrypt" );
531
532 /* cannot use --output with --decrypt-files */
533 if( nfiles == 1 && !ctx->pipemode && output ) {
534 _gpgme_gpg_add_arg( ctx->gpg, "--output" );
535 _gpgme_gpg_add_arg( ctx->gpg, output );
536 }
537
538 while( nfiles-- )
539 _gpgme_gpg_add_arg( ctx->gpg, *input++ );
540
541 rc = _gpgme_gpg_spawn( ctx->gpg, ctx );
542 if( rc ) {
543 ctx->pending = 0;
544 _gpgme_gpg_release( &ctx->gpg );
545 }
546
547 return rc;
548 } /* file_decrypt_start */
549
550
551 static gpgme_error_t
552 decrypt_start( gpgme_ctx_t ctx, gpgme_data_t ciph, gpgme_data_t plain )
553 {
554 int rc = 0;
555
556 fail_on_pending_request( ctx );
557 ctx->pending = 1;
558
559 _gpgme_release_result( ctx );
560 ctx->out_of_core = 0;
561
562 /* create a process object */
563 _gpgme_gpg_release( &ctx->gpg );
564 rc = _gpgme_gpg_new( &ctx->gpg );
565 if( rc )
566 goto leave;
567
568 _gpgme_gpg_set_status_handler( ctx->gpg, decrypt_status_handler, ctx );
569 if( ctx->use_logging )
570 _gpgme_gpg_set_logging_handler( ctx->gpg, ctx );
571 if( ctx->cb.passphrase ) {
572 rc = _gpgme_gpg_set_command_handler( ctx->gpg, decrypt_command_handler, ctx );
573 if ( rc )
574 goto leave;
575 }
576 else if( ctx->passphrase_value ) {
577 rc = _gpgme_add_passphrase( ctx );
578 if( rc )
579 goto leave;
580 }
581
582 /* build the commandline */
583 _gpgme_gpg_add_arg( ctx->gpg, "--decrypt" );
584 /* Check the supplied data */
585 if( !ciph || gpgme_data_get_type( ciph ) == GPGME_DATA_TYPE_NONE ) {
586 rc = mk_error( No_Data );
587 goto leave;
588 }
589 _gpgme_data_set_mode( ciph, GPGME_DATA_MODE_OUT );
590 if( gpgme_data_get_type( plain ) != GPGME_DATA_TYPE_NONE ) {
591 rc = mk_error( Invalid_Value );
592 goto leave;
593 }
594 _gpgme_data_set_mode( plain, GPGME_DATA_MODE_IN );
595
596 /* Tell the gpg object about the data */
597 _gpgme_gpg_add_arg ( ctx->gpg, "--output" );
598 _gpgme_gpg_add_arg ( ctx->gpg, "-" );
599 _gpgme_gpg_add_data( ctx->gpg, plain, 1 );
600 _gpgme_gpg_add_data( ctx->gpg, ciph, 0 );
601
602 /* and kick off the process */
603 rc = _gpgme_gpg_spawn( ctx->gpg, ctx );
604
605 leave:
606 if( rc ) {
607 ctx->pending = 0;
608 _gpgme_gpg_release( &ctx->gpg );
609 }
610 return rc;
611 } /* decrypt_start */
612
613
614 static gpgme_error_t
615 get_decrypt_result( gpgme_ctx_t ctx )
616 {
617 gpgme_error_t err;
618 struct decrypt_result_s * res;
619
620 assert( ctx->result.decrypt );
621 res = ctx->result.decrypt;
622 if( ctx->result_type != RESULT_TYPE_DECRYPT )
623 err = mk_error( General_Error );
624 else if (res->okay || res->sig_okay)
625 err = 0;
626 else if( ctx->out_of_core )
627 err = mk_error( Out_Of_Core );
628 else if( res->no_passphrase )
629 err = mk_error( No_Passphrase );
630 else if( res->bad_passphrase )
631 err = mk_error( Bad_Passphrase );
632 else if( res->no_seckey)
633 err = mk_error( No_Seckey );
634 else if( res->idea_cipher )
635 err = mk_error( Cipher_IDEA );
636 else if( res->failed || (res->file_start != res->file_done) )
637 err = mk_error( Decryption_Failed );
638 else if( !res->okay || !res->no_data )
639 err = mk_error( No_Data );
640 else if( gpgme_get_process_rc( ctx ) )
641 err = mk_error( Internal_GPG_Problem );
642 return err;
643 } /* get_decrypt_result */
644
645
646 /**
647 * gpgme_op_decrypt:
648 * @c: The context
649 * @ciph: ciphertext input
650 * @plain: plaintext output
651 *
652 * This function decrypts @in to @out.
653 * Other parameters are take from the context @c.
654 * The function does wait for the result.
655 *
656 * Return value: 0 on success or an errorcode.
657 **/
658 gpgme_error_t
659 gpgme_op_decrypt( gpgme_ctx_t ctx, gpgme_data_t ciph, gpgme_data_t plain )
660
661 {
662 gpgme_error_t err;
663
664 err = decrypt_start( ctx, ciph, plain );
665 if( !err ) {
666 gpgme_wait( ctx, 1 );
667 ctx->pending = 0;
668 err = get_decrypt_result( ctx );
669 }
670 return err;
671 } /* gpgme_op_decrypt */
672
673
674 gpgme_error_t
675 gpgme_op_file_decrypt( gpgme_ctx_t ctx, const char * ciph, const char * plain )
676 {
677 gpgme_error_t err;
678 const char * files[1];
679
680 files[0] = ciph;
681
682 err = file_decrypt_start( ctx, files, 1, plain );
683 if( !err ) {
684 gpgme_wait( ctx, 1 );
685 err = get_decrypt_result( ctx );
686 ctx->pending = 0;
687 }
688 return err;
689 } /* gpgme_op_file_decrypt */
690
691
692 gpgme_error_t
693 gpgme_op_files_decrypt( gpgme_ctx_t ctx, const char ** files, size_t nfiles )
694 {
695 gpgme_error_t err;
696
697 err = file_decrypt_start( ctx, files, nfiles, NULL );
698 if( !err ) {
699 gpgme_wait( ctx, 1 );
700 err = get_decrypt_result( ctx );
701 ctx->pending = 0;
702 }
703 return err;
704 }
705
706
707 gpgme_error_t
708 gpgme_op_clip_decrypt( gpgme_ctx_t ctx )
709 {
710 gpgme_error_t err;
711 gpgme_data_t ciph = NULL;
712 gpgme_data_t plain = NULL;
713
714 err = gpgme_data_new_from_clipboard (&ciph);
715 if( !err )
716 err = gpgme_data_new( &plain );
717 if( !err )
718 err = gpgme_op_decrypt( ctx, ciph, plain );
719
720 gpgme_data_release_and_set_clipboard( plain );
721 gpgme_data_release( ciph );
722
723 return err;
724 } /* gpgme_op_clip_decrypt */

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26