Check context pointers for null pointer on entry points.
[gpgme.git] / src / verify.c
1 /* verify.c - Signature verification.
2    Copyright (C) 2000 Werner Koch (dd9jn)
3    Copyright (C) 2001, 2002, 2003, 2004, 2005 g10 Code GmbH
4
5    This file is part of GPGME.
6  
7    GPGME is free software; you can redistribute it and/or modify it
8    under the terms of the GNU Lesser General Public License as
9    published by the Free Software Foundation; either version 2.1 of
10    the License, or (at your option) any later version.
11    
12    GPGME is distributed in the hope that it will be useful, but
13    WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15    Lesser General Public License for more details.
16    
17    You should have received a copy of the GNU Lesser General Public
18    License along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20    02111-1307, USA.  */
21
22 #if HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 #include <stdlib.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <assert.h>
29
30 #include "gpgme.h"
31 #include "debug.h"
32 #include "util.h"
33 #include "context.h"
34 #include "ops.h"
35
36 \f
37 typedef struct
38 {
39   struct _gpgme_op_verify_result result;
40
41   gpgme_signature_t current_sig;
42   int did_prepare_new_sig;
43   int only_newsig_seen;
44   int plaintext_seen;
45 } *op_data_t;
46
47
48 static void
49 release_op_data (void *hook)
50 {
51   op_data_t opd = (op_data_t) hook;
52   gpgme_signature_t sig = opd->result.signatures;
53
54   while (sig)
55     {
56       gpgme_signature_t next = sig->next;
57       gpgme_sig_notation_t notation = sig->notations;
58
59       while (notation)
60         {
61           gpgme_sig_notation_t next_nota = notation->next;
62
63           _gpgme_sig_notation_free (notation);
64           notation = next_nota;
65         }
66
67       if (sig->fpr)
68         free (sig->fpr);
69       if (sig->pka_address)
70         free (sig->pka_address);
71       free (sig);
72       sig = next;
73     }
74
75   if (opd->result.file_name)
76     free (opd->result.file_name);
77 }
78
79
80 gpgme_verify_result_t
81 gpgme_op_verify_result (gpgme_ctx_t ctx)
82 {
83   void *hook;
84   op_data_t opd;
85   gpgme_error_t err;
86
87   TRACE_BEG (DEBUG_CTX, "gpgme_op_verify_result", ctx);
88   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
89   opd = hook;
90   if (err || !opd)
91     {
92       TRACE_SUC0 ("result=(null)");
93       return NULL;
94     }
95
96   if (_gpgme_debug_trace ())
97     {
98       gpgme_signature_t sig = opd->result.signatures;
99       int i = 0;
100
101       while (sig)
102         {
103           TRACE_LOG4 ("sig[%i] = fpr %s, summary 0x%x, status %s",
104                       i, sig->fpr, sig->summary, gpg_strerror (sig->status));
105           TRACE_LOG6 ("sig[%i] = timestamps 0x%x/0x%x flags:%s%s%s",
106                       i, sig->timestamp, sig->exp_timestamp,
107                       sig->wrong_key_usage ? "wrong key usage" : "",
108                       sig->pka_trust == 1 ? "pka bad"
109                       : (sig->pka_trust == 2 ? "pka_okay" : "pka RFU"),
110                       sig->chain_model ? "chain model" : "");
111           TRACE_LOG5 ("sig[%i] = validity 0x%x (%s), algos %s/%s",
112                       i, sig->validity, gpg_strerror (sig->validity_reason),
113                       gpgme_pubkey_algo_name (sig->pubkey_algo),
114                       gpgme_hash_algo_name (sig->hash_algo));
115           if (sig->pka_address)
116             {
117               TRACE_LOG2 ("sig[%i] = PKA address %s", i, sig->pka_address);
118             }
119           if (sig->notations)
120             {
121               TRACE_LOG1 ("sig[%i] = has notations (not shown)", i);
122             }     
123           sig = sig->next;
124           i++;
125         }
126     }
127
128   TRACE_SUC1 ("result=%p", &opd->result);
129   return &opd->result;
130 }
131
132 \f
133 /* Build a summary vector from RESULT. */
134 static void
135 calc_sig_summary (gpgme_signature_t sig)
136 {
137   unsigned long sum = 0;
138   
139   /* Calculate the red/green flag.  */
140   if (sig->validity == GPGME_VALIDITY_FULL
141       || sig->validity == GPGME_VALIDITY_ULTIMATE)
142     {
143       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
144           || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
145           || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
146         sum |= GPGME_SIGSUM_GREEN;
147     }
148   else if (sig->validity == GPGME_VALIDITY_NEVER)
149     {
150       if (gpg_err_code (sig->status) == GPG_ERR_NO_ERROR
151           || gpg_err_code (sig->status) == GPG_ERR_SIG_EXPIRED
152           || gpg_err_code (sig->status) == GPG_ERR_KEY_EXPIRED)
153         sum |= GPGME_SIGSUM_RED;
154     }
155   else if (gpg_err_code (sig->status) == GPG_ERR_BAD_SIGNATURE)
156     sum |= GPGME_SIGSUM_RED;
157
158
159   /* FIXME: handle the case when key and message are expired. */
160   switch (gpg_err_code (sig->status))
161     {
162     case GPG_ERR_SIG_EXPIRED:
163       sum |= GPGME_SIGSUM_SIG_EXPIRED;
164       break;
165
166     case GPG_ERR_KEY_EXPIRED:
167       sum |= GPGME_SIGSUM_KEY_EXPIRED;
168       break;
169
170     case GPG_ERR_NO_PUBKEY:
171       sum |= GPGME_SIGSUM_KEY_MISSING;
172       break;
173
174     case GPG_ERR_BAD_SIGNATURE:
175     case GPG_ERR_NO_ERROR:
176       break;
177
178     default:
179       sum |= GPGME_SIGSUM_SYS_ERROR;
180       break;
181     }
182   
183   /* Now look at the certain reason codes.  */
184   switch (gpg_err_code (sig->validity_reason))
185     {
186     case GPG_ERR_CRL_TOO_OLD:
187       if (sig->validity == GPGME_VALIDITY_UNKNOWN)
188         sum |= GPGME_SIGSUM_CRL_TOO_OLD;
189       break;
190         
191     case GPG_ERR_CERT_REVOKED:
192       sum |= GPGME_SIGSUM_KEY_REVOKED;
193       break;
194
195     default:
196       break;
197     }
198
199   /* Check other flags. */
200   if (sig->wrong_key_usage)
201     sum |= GPGME_SIGSUM_BAD_POLICY;
202   
203   /* Set the valid flag when the signature is unquestionable
204      valid.  (The test is identical to if(sum == GPGME_SIGSUM_GREEN)). */
205   if ((sum & GPGME_SIGSUM_GREEN) && !(sum & ~GPGME_SIGSUM_GREEN))
206     sum |= GPGME_SIGSUM_VALID;
207   
208   sig->summary = sum;
209 }
210   
211
212 static gpgme_error_t
213 prepare_new_sig (op_data_t opd)
214 {
215   gpgme_signature_t sig;
216
217   if (opd->only_newsig_seen && opd->current_sig)
218     {
219       /* We have only seen the NEWSIG status and nothing else - we
220          better skip this signature therefore and reuse it for the
221          next possible signature. */
222       sig = opd->current_sig;
223       memset (sig, 0, sizeof *sig);
224       assert (opd->result.signatures == sig);
225     }
226   else
227     {
228       sig = calloc (1, sizeof (*sig));
229       if (!sig)
230         return gpg_error_from_syserror ();
231       if (!opd->result.signatures)
232         opd->result.signatures = sig;
233       if (opd->current_sig)
234         opd->current_sig->next = sig;
235       opd->current_sig = sig;
236     }
237   opd->did_prepare_new_sig = 1;
238   opd->only_newsig_seen = 0;
239   return 0;
240 }
241
242 static gpgme_error_t
243 parse_new_sig (op_data_t opd, gpgme_status_code_t code, char *args)
244 {
245   gpgme_signature_t sig;
246   char *end = strchr (args, ' ');
247   char *tail;
248
249   if (end)
250     {
251       *end = '\0';
252       end++;
253     }
254
255   if (!opd->did_prepare_new_sig)
256     {
257       gpg_error_t err;
258
259       err = prepare_new_sig (opd);
260       if (err)
261         return err;
262     }
263   assert (opd->did_prepare_new_sig);
264   opd->did_prepare_new_sig = 0;
265
266   assert (opd->current_sig);
267   sig = opd->current_sig;
268
269   /* FIXME: We should set the source of the state.  */
270   switch (code)
271     {
272     case GPGME_STATUS_GOODSIG:
273       sig->status = gpg_error (GPG_ERR_NO_ERROR);
274       break;
275
276     case GPGME_STATUS_EXPSIG:
277       sig->status = gpg_error (GPG_ERR_SIG_EXPIRED);
278       break;
279
280     case GPGME_STATUS_EXPKEYSIG:
281       sig->status = gpg_error (GPG_ERR_KEY_EXPIRED);
282       break;
283
284     case GPGME_STATUS_BADSIG:
285       sig->status = gpg_error (GPG_ERR_BAD_SIGNATURE);
286       break;
287
288     case GPGME_STATUS_REVKEYSIG:
289       sig->status = gpg_error (GPG_ERR_CERT_REVOKED);
290       break;
291
292     case GPGME_STATUS_ERRSIG:
293       /* Parse the pubkey algo.  */
294       if (!end)
295         goto parse_err_sig_fail;
296       gpg_err_set_errno (0);
297       sig->pubkey_algo = strtol (end, &tail, 0);
298       if (errno || end == tail || *tail != ' ')
299         goto parse_err_sig_fail;
300       end = tail;
301       while (*end == ' ')
302         end++;
303      
304       /* Parse the hash algo.  */
305       if (!*end)
306         goto parse_err_sig_fail;
307       gpg_err_set_errno (0);
308       sig->hash_algo = strtol (end, &tail, 0);
309       if (errno || end == tail || *tail != ' ')
310         goto parse_err_sig_fail;
311       end = tail;
312       while (*end == ' ')
313         end++;
314
315       /* Skip the sig class.  */
316       end = strchr (end, ' ');
317       if (!end)
318         goto parse_err_sig_fail;
319       while (*end == ' ')
320         end++;
321
322       /* Parse the timestamp.  */
323       sig->timestamp = _gpgme_parse_timestamp (end, &tail);
324       if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
325         return gpg_error (GPG_ERR_INV_ENGINE);
326       end = tail;
327       while (*end == ' ')
328         end++;
329       
330       /* Parse the return code.  */
331       if (end[0] && (!end[1] || end[1] == ' '))
332         {
333           switch (end[0])
334             {
335             case '4':
336               sig->status = gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
337               break;
338               
339             case '9':
340               sig->status = gpg_error (GPG_ERR_NO_PUBKEY);
341               break;
342               
343             default:
344               sig->status = gpg_error (GPG_ERR_GENERAL);
345             }
346         }
347       else
348         goto parse_err_sig_fail;
349
350       goto parse_err_sig_ok;
351       
352     parse_err_sig_fail:
353       sig->status = gpg_error (GPG_ERR_GENERAL);
354     parse_err_sig_ok:
355       break;
356       
357     default:
358       return gpg_error (GPG_ERR_GENERAL);
359     }
360
361   if (*args)
362     {
363       sig->fpr = strdup (args);
364       if (!sig->fpr)
365         return gpg_error_from_syserror ();
366     }
367   return 0;
368 }
369
370
371 static gpgme_error_t
372 parse_valid_sig (gpgme_signature_t sig, char *args)
373 {
374   char *end = strchr (args, ' ');
375   if (end)
376     {
377       *end = '\0';
378       end++;
379     }
380
381   if (!*args)
382     /* We require at least the fingerprint.  */
383     return gpg_error (GPG_ERR_GENERAL);
384
385   if (sig->fpr)
386     free (sig->fpr);
387   sig->fpr = strdup (args);
388   if (!sig->fpr)
389     return gpg_error_from_syserror ();
390
391   /* Skip the creation date.  */
392   end = strchr (end, ' ');
393   if (end)
394     {
395       char *tail;
396
397       sig->timestamp = _gpgme_parse_timestamp (end, &tail);
398       if (sig->timestamp == -1 || end == tail || (*tail && *tail != ' '))
399         return gpg_error (GPG_ERR_INV_ENGINE);
400       end = tail;
401      
402       sig->exp_timestamp = _gpgme_parse_timestamp (end, &tail);
403       if (sig->exp_timestamp == -1 || end == tail || (*tail && *tail != ' '))
404         return gpg_error (GPG_ERR_INV_ENGINE);
405       end = tail;
406
407       while (*end == ' ')
408         end++;
409       /* Skip the signature version.  */
410       end = strchr (end, ' ');
411       if (end)
412         {
413           while (*end == ' ')
414             end++;
415
416           /* Skip the reserved field.  */
417           end = strchr (end, ' ');
418           if (end)
419             {
420               /* Parse the pubkey algo.  */
421               gpg_err_set_errno (0);
422               sig->pubkey_algo = strtol (end, &tail, 0);
423               if (errno || end == tail || *tail != ' ')
424                 return gpg_error (GPG_ERR_INV_ENGINE);
425               end = tail;
426
427               while (*end == ' ')
428                 end++;
429
430               if (*end)
431                 {
432                   /* Parse the hash algo.  */
433
434                   gpg_err_set_errno (0);
435                   sig->hash_algo = strtol (end, &tail, 0);
436                   if (errno || end == tail || *tail != ' ')
437                     return gpg_error (GPG_ERR_INV_ENGINE);
438                   end = tail;
439                 }
440             }
441         }
442     }
443   return 0;
444 }
445
446
447 static gpgme_error_t
448 parse_notation (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
449 {
450   gpgme_error_t err;
451   gpgme_sig_notation_t *lastp = &sig->notations;
452   gpgme_sig_notation_t notation = sig->notations;
453   char *end = strchr (args, ' ');
454
455   if (end)
456     *end = '\0';
457
458   if (code == GPGME_STATUS_NOTATION_NAME || code == GPGME_STATUS_POLICY_URL)
459     {
460       /* FIXME: We could keep a pointer to the last notation in the list.  */
461       while (notation && notation->value)
462         {
463           lastp = &notation->next;
464           notation = notation->next;
465         }
466
467       if (notation)
468         /* There is another notation name without data for the
469            previous one.  The crypto backend misbehaves.  */
470         return gpg_error (GPG_ERR_INV_ENGINE);
471
472       err = _gpgme_sig_notation_create (&notation, NULL, 0, NULL, 0, 0);
473       if (err)
474         return err;
475
476       if (code == GPGME_STATUS_NOTATION_NAME)
477         {
478           err = _gpgme_decode_percent_string (args, &notation->name, 0, 0);
479           if (err)
480             {
481               _gpgme_sig_notation_free (notation);
482               return err;
483             }
484
485           notation->name_len = strlen (notation->name);
486
487           /* FIXME: For now we fake the human-readable flag.  The
488              critical flag can not be reported as it is not
489              provided.  */
490           notation->flags = GPGME_SIG_NOTATION_HUMAN_READABLE;
491           notation->human_readable = 1;
492         }
493       else
494         {
495           /* This is a policy URL.  */
496
497           err = _gpgme_decode_percent_string (args, &notation->value, 0, 0);
498           if (err)
499             {
500               _gpgme_sig_notation_free (notation);
501               return err;
502             }
503
504           notation->value_len = strlen (notation->value);
505         }
506       *lastp = notation;
507     }
508   else if (code == GPGME_STATUS_NOTATION_DATA)
509     {
510       int len = strlen (args) + 1;
511       char *dest;
512
513       /* FIXME: We could keep a pointer to the last notation in the list.  */
514       while (notation && notation->next)
515         {
516           lastp = &notation->next;
517           notation = notation->next;
518         }
519
520       if (!notation || !notation->name)
521         /* There is notation data without a previous notation
522            name.  The crypto backend misbehaves.  */
523         return gpg_error (GPG_ERR_INV_ENGINE);
524       
525       if (!notation->value)
526         {
527           dest = notation->value = malloc (len);
528           if (!dest)
529             return gpg_error_from_syserror ();
530         }
531       else
532         {
533           int cur_len = strlen (notation->value);
534           dest = realloc (notation->value, len + strlen (notation->value));
535           if (!dest)
536             return gpg_error_from_syserror ();
537           notation->value = dest;
538           dest += cur_len;
539         }
540       
541       err = _gpgme_decode_percent_string (args, &dest, len, 0);
542       if (err)
543         return err;
544
545       notation->value_len += strlen (dest);
546     }
547   else
548     return gpg_error (GPG_ERR_INV_ENGINE);
549   return 0;
550 }
551
552
553 static gpgme_error_t
554 parse_trust (gpgme_signature_t sig, gpgme_status_code_t code, char *args)
555 {
556   char *end = strchr (args, ' ');
557
558   if (end)
559     *end = '\0';
560
561   switch (code)
562     {
563     case GPGME_STATUS_TRUST_UNDEFINED:
564     default:
565       sig->validity = GPGME_VALIDITY_UNKNOWN;
566       break;
567
568     case GPGME_STATUS_TRUST_NEVER:
569       sig->validity = GPGME_VALIDITY_NEVER;
570       break;
571
572     case GPGME_STATUS_TRUST_MARGINAL:
573       sig->validity = GPGME_VALIDITY_MARGINAL;
574       break;
575
576     case GPGME_STATUS_TRUST_FULLY:
577     case GPGME_STATUS_TRUST_ULTIMATE:
578       sig->validity = GPGME_VALIDITY_FULL;
579       break;
580     }
581
582   sig->validity_reason = 0;
583   sig->chain_model = 0;
584   if (*args)
585     {
586       sig->validity_reason = atoi (args);
587       while (*args && *args != ' ')
588         args++;
589       if (*args)
590         {
591           while (*args == ' ')
592             args++;
593           if (!strncmp (args, "chain", 2) && (args[2] == ' ' || !args[2]))
594             sig->chain_model = 1;
595         }
596     }
597
598   return 0;
599 }
600
601
602 /* Parse an error status line and if SET_STATUS is true update the
603    result status as appropriate.  With SET_STATUS being false, only
604    check for an error.  */
605 static gpgme_error_t
606 parse_error (gpgme_signature_t sig, char *args, int set_status)
607 {
608   gpgme_error_t err;
609   char *where = strchr (args, ' ');
610   char *which;
611
612   if (where)
613     {
614       *where = '\0';
615       which = where + 1;
616
617       where = strchr (which, ' ');
618       if (where)
619         *where = '\0';
620
621       where = args;      
622     }
623   else
624     return gpg_error (GPG_ERR_INV_ENGINE);
625
626   err = atoi (which);
627
628   if (!strcmp (where, "proc_pkt.plaintext")
629       && gpg_err_code (err) == GPG_ERR_BAD_DATA)
630     {
631       /* This indicates a double plaintext.  The only solid way to
632          handle this is by failing the oepration.  */
633       return gpg_error (GPG_ERR_BAD_DATA);
634     }
635   else if (!set_status)
636     ;
637   else if (!strcmp (where, "verify.findkey"))
638     sig->status = err;
639   else if (!strcmp (where, "verify.keyusage")
640            && gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
641     sig->wrong_key_usage = 1;
642
643   return 0;
644 }
645
646
647 gpgme_error_t
648 _gpgme_verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
649 {
650   gpgme_ctx_t ctx = (gpgme_ctx_t) priv;
651   gpgme_error_t err;
652   void *hook;
653   op_data_t opd;
654   gpgme_signature_t sig;
655   char *end;
656
657   err = _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook, -1, NULL);
658   opd = hook;
659   if (err)
660     return err;
661
662   sig = opd->current_sig;
663
664   switch (code)
665     {
666     case GPGME_STATUS_NEWSIG:
667       if (sig)
668         calc_sig_summary (sig);
669       err = prepare_new_sig (opd);
670       opd->only_newsig_seen = 1;
671       return err;
672
673     case GPGME_STATUS_GOODSIG:
674     case GPGME_STATUS_EXPSIG:
675     case GPGME_STATUS_EXPKEYSIG:
676     case GPGME_STATUS_BADSIG:
677     case GPGME_STATUS_ERRSIG:
678     case GPGME_STATUS_REVKEYSIG:
679       if (sig && !opd->did_prepare_new_sig)
680         calc_sig_summary (sig);
681       opd->only_newsig_seen = 0;
682       return parse_new_sig (opd, code, args);
683
684     case GPGME_STATUS_VALIDSIG:
685       opd->only_newsig_seen = 0;
686       return sig ? parse_valid_sig (sig, args)
687         : gpg_error (GPG_ERR_INV_ENGINE);
688
689     case GPGME_STATUS_NODATA:
690       opd->only_newsig_seen = 0;
691       if (!sig)
692         return gpg_error (GPG_ERR_NO_DATA);
693       sig->status = gpg_error (GPG_ERR_NO_DATA);
694       break;
695
696     case GPGME_STATUS_UNEXPECTED:
697       opd->only_newsig_seen = 0;
698       if (!sig)
699         return gpg_error (GPG_ERR_GENERAL);
700       sig->status = gpg_error (GPG_ERR_NO_DATA);
701       break;
702
703     case GPGME_STATUS_NOTATION_NAME:
704     case GPGME_STATUS_NOTATION_DATA:
705     case GPGME_STATUS_POLICY_URL:
706       opd->only_newsig_seen = 0;
707       return sig ? parse_notation (sig, code, args)
708         : gpg_error (GPG_ERR_INV_ENGINE);
709
710     case GPGME_STATUS_TRUST_UNDEFINED:
711     case GPGME_STATUS_TRUST_NEVER:
712     case GPGME_STATUS_TRUST_MARGINAL:
713     case GPGME_STATUS_TRUST_FULLY:
714     case GPGME_STATUS_TRUST_ULTIMATE:
715       opd->only_newsig_seen = 0;
716       return sig ? parse_trust (sig, code, args)
717         : gpg_error (GPG_ERR_INV_ENGINE);
718
719     case GPGME_STATUS_PKA_TRUST_BAD:
720     case GPGME_STATUS_PKA_TRUST_GOOD:
721       opd->only_newsig_seen = 0;
722       /* Check that we only get one of these status codes per
723          signature; if not the crypto backend misbehaves.  */
724       if (!sig || sig->pka_trust || sig->pka_address)
725         return gpg_error (GPG_ERR_INV_ENGINE);
726       sig->pka_trust = code == GPGME_STATUS_PKA_TRUST_GOOD? 2 : 1;
727       end = strchr (args, ' ');
728       if (end)
729         *end = 0;
730       sig->pka_address = strdup (args);
731       break;
732
733     case GPGME_STATUS_ERROR:
734       opd->only_newsig_seen = 0;
735       /* Some  error stati are informational, so we don't return an
736          error code if we are not ready to process this status.  */
737       return parse_error (sig, args, !!sig );
738
739     case GPGME_STATUS_EOF:
740       if (sig && !opd->did_prepare_new_sig)
741         calc_sig_summary (sig);
742       if (opd->only_newsig_seen && sig)
743         {
744           gpgme_signature_t sig2;
745           /* The last signature has no valid information - remove it
746              from the list. */
747           assert (!sig->next);
748           if (sig == opd->result.signatures)
749             opd->result.signatures = NULL;
750           else
751             {
752               for (sig2 = opd->result.signatures; sig2; sig2 = sig2->next)
753                 if (sig2->next == sig)
754                   {
755                     sig2->next = NULL;
756                     break;
757                   }
758             }
759           /* Note that there is no need to release the members of SIG
760              because we won't be here if they have been set. */
761           free (sig);
762           opd->current_sig = NULL;
763         }
764       opd->only_newsig_seen = 0;
765       break;
766
767     case GPGME_STATUS_PLAINTEXT:
768       if (++opd->plaintext_seen > 1)
769         return gpg_error (GPG_ERR_BAD_DATA);
770       err = _gpgme_parse_plaintext (args, &opd->result.file_name);
771       if (err)
772         return err;
773
774     default:
775       break;
776     }
777   return 0;
778 }
779
780
781 static gpgme_error_t
782 verify_status_handler (void *priv, gpgme_status_code_t code, char *args)
783 {
784   gpgme_error_t err;
785
786   err = _gpgme_progress_status_handler (priv, code, args);
787   if (!err)
788     err = _gpgme_verify_status_handler (priv, code, args);
789   return err;
790 }
791
792
793 gpgme_error_t
794 _gpgme_op_verify_init_result (gpgme_ctx_t ctx)
795 {  
796   void *hook;
797   op_data_t opd;
798
799   return _gpgme_op_data_lookup (ctx, OPDATA_VERIFY, &hook,
800                                 sizeof (*opd), release_op_data);
801 }
802
803
804 static gpgme_error_t
805 verify_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t sig,
806               gpgme_data_t signed_text, gpgme_data_t plaintext)
807 {
808   gpgme_error_t err;
809
810   err = _gpgme_op_reset (ctx, synchronous);
811   if (err)
812     return err;
813
814   err = _gpgme_op_verify_init_result (ctx);
815   if (err)
816     return err;
817
818   _gpgme_engine_set_status_handler (ctx->engine, verify_status_handler, ctx);
819
820   if (!sig)
821     return gpg_error (GPG_ERR_NO_DATA);
822   if (!signed_text && !plaintext)
823     return gpg_error (GPG_ERR_INV_VALUE);
824
825   return _gpgme_engine_op_verify (ctx->engine, sig, signed_text, plaintext);
826 }
827
828
829 /* Decrypt ciphertext CIPHER and make a signature verification within
830    CTX and store the resulting plaintext in PLAIN.  */
831 gpgme_error_t
832 gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig,
833                        gpgme_data_t signed_text, gpgme_data_t plaintext)
834 {
835   gpg_error_t err;
836   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify_start", ctx,
837               "sig=%p, signed_text=%p, plaintext=%p",
838               sig, signed_text, plaintext);
839
840   if (!ctx)
841     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
842
843   err = verify_start (ctx, 0, sig, signed_text, plaintext);
844   return TRACE_ERR (err);
845 }
846
847
848 /* Decrypt ciphertext CIPHER and make a signature verification within
849    CTX and store the resulting plaintext in PLAIN.  */
850 gpgme_error_t
851 gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text,
852                  gpgme_data_t plaintext)
853 {
854   gpgme_error_t err;
855
856   TRACE_BEG3 (DEBUG_CTX, "gpgme_op_verify", ctx,
857               "sig=%p, signed_text=%p, plaintext=%p",
858               sig, signed_text, plaintext);
859
860   if (!ctx)
861     return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE));
862
863   err = verify_start (ctx, 1, sig, signed_text, plaintext);
864   if (!err)
865     err = _gpgme_wait_one (ctx);
866   return TRACE_ERR (err);
867 }
868
869 \f
870 /* Compatibility interfaces.  */
871
872 /* Get the key used to create signature IDX in CTX and return it in
873    R_KEY.  */
874 gpgme_error_t
875 gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key)
876 {
877   gpgme_verify_result_t result;
878   gpgme_signature_t sig;
879
880   if (!ctx)
881     return gpg_error (GPG_ERR_INV_VALUE);
882
883   result = gpgme_op_verify_result (ctx);
884   sig = result->signatures;
885
886   while (sig && idx)
887     {
888       sig = sig->next;
889       idx--;
890     }
891   if (!sig || idx)
892     return gpg_error (GPG_ERR_EOF);
893
894   return gpgme_get_key (ctx, sig->fpr, r_key, 0);
895 }
896
897
898 /* Retrieve the signature status of signature IDX in CTX after a
899    successful verify operation in R_STAT (if non-null).  The creation
900    time stamp of the signature is returned in R_CREATED (if non-null).
901    The function returns a string containing the fingerprint.  */
902 const char *
903 gpgme_get_sig_status (gpgme_ctx_t ctx, int idx,
904                       _gpgme_sig_stat_t *r_stat, time_t *r_created)
905 {
906   gpgme_verify_result_t result;
907   gpgme_signature_t sig;
908
909   result = gpgme_op_verify_result (ctx);
910   sig = result->signatures;
911
912   while (sig && idx)
913     {
914       sig = sig->next;
915       idx--;
916     }
917   if (!sig || idx)
918     return NULL;
919
920   if (r_stat)
921     {
922       switch (gpg_err_code (sig->status))
923         {
924         case GPG_ERR_NO_ERROR:
925           *r_stat = GPGME_SIG_STAT_GOOD;
926           break;
927           
928         case GPG_ERR_BAD_SIGNATURE:
929           *r_stat = GPGME_SIG_STAT_BAD;
930           break;
931           
932         case GPG_ERR_NO_PUBKEY:
933           *r_stat = GPGME_SIG_STAT_NOKEY;
934           break;
935           
936         case GPG_ERR_NO_DATA:
937           *r_stat = GPGME_SIG_STAT_NOSIG;
938           break;
939           
940         case GPG_ERR_SIG_EXPIRED:
941           *r_stat = GPGME_SIG_STAT_GOOD_EXP;
942           break;
943           
944         case GPG_ERR_KEY_EXPIRED:
945           *r_stat = GPGME_SIG_STAT_GOOD_EXPKEY;
946           break;
947           
948         default:
949           *r_stat = GPGME_SIG_STAT_ERROR;
950           break;
951         }
952     }
953   if (r_created)
954     *r_created = sig->timestamp;
955   return sig->fpr;
956 }
957
958
959 /* Retrieve certain attributes of a signature.  IDX is the index
960    number of the signature after a successful verify operation.  WHAT
961    is an attribute where GPGME_ATTR_EXPIRE is probably the most useful
962    one.  WHATIDX is to be passed as 0 for most attributes . */
963 unsigned long 
964 gpgme_get_sig_ulong_attr (gpgme_ctx_t ctx, int idx,
965                           _gpgme_attr_t what, int whatidx)
966 {
967   gpgme_verify_result_t result;
968   gpgme_signature_t sig;
969
970   result = gpgme_op_verify_result (ctx);
971   sig = result->signatures;
972
973   while (sig && idx)
974     {
975       sig = sig->next;
976       idx--;
977     }
978   if (!sig || idx)
979     return 0;
980
981   switch (what)
982     {
983     case GPGME_ATTR_CREATED:
984       return sig->timestamp;
985
986     case GPGME_ATTR_EXPIRE:
987       return sig->exp_timestamp;
988
989     case GPGME_ATTR_VALIDITY:
990       return (unsigned long) sig->validity;
991
992     case GPGME_ATTR_SIG_STATUS:
993       switch (gpg_err_code (sig->status))
994         {
995         case GPG_ERR_NO_ERROR:
996           return GPGME_SIG_STAT_GOOD;
997           
998         case GPG_ERR_BAD_SIGNATURE:
999           return GPGME_SIG_STAT_BAD;
1000           
1001         case GPG_ERR_NO_PUBKEY:
1002           return GPGME_SIG_STAT_NOKEY;
1003           
1004         case GPG_ERR_NO_DATA:
1005           return GPGME_SIG_STAT_NOSIG;
1006           
1007         case GPG_ERR_SIG_EXPIRED:
1008           return GPGME_SIG_STAT_GOOD_EXP;
1009           
1010         case GPG_ERR_KEY_EXPIRED:
1011           return GPGME_SIG_STAT_GOOD_EXPKEY;
1012           
1013         default:
1014           return GPGME_SIG_STAT_ERROR;
1015         }
1016
1017     case GPGME_ATTR_SIG_SUMMARY:
1018       return sig->summary;
1019
1020     default:
1021       break;
1022     }
1023   return 0;
1024 }
1025
1026
1027 const char *
1028 gpgme_get_sig_string_attr (gpgme_ctx_t ctx, int idx,
1029                            _gpgme_attr_t what, int whatidx)
1030 {
1031   gpgme_verify_result_t result;
1032   gpgme_signature_t sig;
1033
1034   result = gpgme_op_verify_result (ctx);
1035   sig = result->signatures;
1036
1037   while (sig && idx)
1038     {
1039       sig = sig->next;
1040       idx--;
1041     }
1042   if (!sig || idx)
1043     return NULL;
1044
1045   switch (what)
1046     {
1047     case GPGME_ATTR_FPR:
1048       return sig->fpr;
1049
1050     case GPGME_ATTR_ERRTOK:
1051       if (whatidx == 1)
1052         return sig->wrong_key_usage ? "Wrong_Key_Usage" : "";
1053       else
1054         return "";
1055     default:
1056       break;
1057     }
1058
1059   return NULL;
1060 }