91f04108eb96f4ac4ffebba998f710d5d25ade40
[gnupg.git] / tools / gpg-card-tool.c
1 /* gpg-card-tool.c - An interactive tool to work with cards.
2  * Copyright (C) 2019 g10 Code GmbH Werner Koch
3  *
4  * This file is part of GnuPG.
5  *
6  * This file is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This file 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 Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <https://gnu.org/licenses/>.
18  * SPDX-License-Identifier: GPL-3.0+
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #ifdef HAVE_LIBREADLINE
26 # define GNUPG_LIBREADLINE_H_INCLUDED
27 # include <readline/readline.h>
28 #endif /*HAVE_LIBREADLINE*/
29
30 #include "../common/util.h"
31 #include "../common/status.h"
32 #include "../common/i18n.h"
33 #include "../common/init.h"
34 #include "../common/sysutils.h"
35 #include "../common/asshelp.h"
36 #include "../common/userids.h"
37 #include "../common/ccparray.h"
38 #include "../common/exectool.h"
39 #include "../common/ttyio.h"
40
41 #define CONTROL_D ('D' - 'A' + 1)
42
43 /* Constants to identify the commands and options. */
44 enum cmd_and_opt_values
45   {
46     aNull = 0,
47
48     oQuiet      = 'q',
49     oVerbose    = 'v',
50
51     oDebug      = 500,
52
53     oGpgProgram,
54     oGpgsmProgram,
55     oStatusFD,
56     oWithColons,
57
58     oDummy
59   };
60
61
62 /* The list of commands and options. */
63 static ARGPARSE_OPTS opts[] = {
64   ARGPARSE_group (300, ("@Commands:\n ")),
65
66   ARGPARSE_group (301, ("@\nOptions:\n ")),
67
68   ARGPARSE_s_n (oVerbose, "verbose", ("verbose")),
69   ARGPARSE_s_n (oQuiet, "quiet",  ("be somewhat more quiet")),
70   ARGPARSE_s_s (oDebug, "debug", "@"),
71   ARGPARSE_s_s (oGpgProgram, "gpg", "@"),
72   ARGPARSE_s_s (oGpgsmProgram, "gpgsm", "@"),
73   ARGPARSE_s_i (oStatusFD, "status-fd", N_("|FD|write status info to this FD")),
74   ARGPARSE_s_n (oWithColons, "with-colons", "@"),
75
76   ARGPARSE_end ()
77 };
78
79 /* Debug values and macros.  */
80 #define DBG_IPC_VALUE      1024 /* Debug assuan communication.  */
81 #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */
82
83
84 /* The list of supported debug flags.  */
85 static struct debug_flags_s debug_flags [] =
86   {
87     { DBG_IPC_VALUE    , "ipc"     },
88     { DBG_EXTPROG_VALUE, "extprog" },
89     { 0, NULL }
90   };
91
92
93
94 /* We keep all global options in the structure OPT.  */
95 struct
96 {
97   int verbose;
98   unsigned int debug;
99   int quiet;
100   int with_colons;
101   const char *gpg_program;
102   const char *gpgsm_program;
103 } opt;
104
105
106 static void wrong_args (const char *text) GPGRT_ATTR_NORETURN;
107 static void interactive_loop (void);
108 #ifdef HAVE_LIBREADLINE
109 static char **command_completion (const char *text, int start, int end);
110 #endif /*HAVE_LIBREADLINE*/
111
112
113 \f
114 /* Print usage information and provide strings for help. */
115 static const char *
116 my_strusage( int level )
117 {
118   const char *p;
119
120   switch (level)
121     {
122     case 11: p = "gpg-card-tool"; break;
123     case 12: p = "@GNUPG@"; break;
124     case 13: p = VERSION; break;
125     case 17: p = PRINTABLE_OS_NAME; break;
126     case 19: p = ("Please report bugs to <@EMAIL@>.\n"); break;
127
128     case 1:
129     case 40:
130       p = ("Usage: gpg-card-tool [command] [options] [args] (-h for help)");
131       break;
132     case 41:
133       p = ("Syntax: gpg-card-tool [command] [options] [args]\n"
134            "Tool to configure cards and tokens\n");
135       break;
136
137     default: p = NULL; break;
138     }
139   return p;
140 }
141
142
143 static void
144 wrong_args (const char *text)
145 {
146   es_fprintf (es_stderr, _("usage: %s [options] %s\n"), strusage (11), text);
147   exit (2);
148 }
149
150
151 \f
152 /* Command line parsing.  */
153 static enum cmd_and_opt_values
154 parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts)
155 {
156   enum cmd_and_opt_values cmd = 0;
157   int no_more_options = 0;
158
159   while (!no_more_options && optfile_parse (NULL, NULL, NULL, pargs, popts))
160     {
161       switch (pargs->r_opt)
162         {
163         case oQuiet:     opt.quiet = 1; break;
164         case oVerbose:   opt.verbose++; break;
165         case oDebug:
166           if (parse_debug_flag (pargs->r.ret_str, &opt.debug, debug_flags))
167             {
168               pargs->r_opt = ARGPARSE_INVALID_ARG;
169               pargs->err = ARGPARSE_PRINT_ERROR;
170             }
171           break;
172
173         case oGpgProgram:
174           opt.gpg_program = pargs->r.ret_str;
175           break;
176         case oGpgsmProgram:
177           opt.gpgsm_program = pargs->r.ret_str;
178           break;
179         case oStatusFD:
180           gnupg_set_status_fd (translate_sys2libc_fd_int (pargs->r.ret_int, 1));
181           break;
182         case oWithColons:
183           opt.with_colons = 1;
184           break;
185
186         default: pargs->err = 2; break;
187         }
188     }
189
190   return cmd;
191 }
192
193
194 \f
195 /* gpg-card-tool main. */
196 int
197 main (int argc, char **argv)
198 {
199   gpg_error_t err;
200   ARGPARSE_ARGS pargs;
201   enum cmd_and_opt_values cmd;
202
203   gnupg_reopen_std ("gpg-card-tool");
204   set_strusage (my_strusage);
205   gnupg_rl_initialize ();
206   log_set_prefix ("gpg-card-tool", GPGRT_LOG_WITH_PREFIX);
207
208   /* Make sure that our subsystems are ready.  */
209   i18n_init();
210   init_common_subsystems (&argc, &argv);
211
212   assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT);
213   setup_libassuan_logging (&opt.debug, NULL);
214
215   /* Parse the command line. */
216   pargs.argc  = &argc;
217   pargs.argv  = &argv;
218   pargs.flags = ARGPARSE_FLAG_KEEP;
219   cmd = parse_arguments (&pargs, opts);
220
221   if (log_get_errorcount (0))
222     exit (2);
223
224   /* Print a warning if an argument looks like an option.  */
225   if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN))
226     {
227       int i;
228
229       for (i=0; i < argc; i++)
230         if (argv[i][0] == '-' && argv[i][1] == '-')
231           log_info (("NOTE: '%s' is not considered an option\n"), argv[i]);
232     }
233
234   /* Set defaults for non given options.  */
235   if (!opt.gpg_program)
236     opt.gpg_program = gnupg_module_name (GNUPG_MODULE_NAME_GPG);
237   if (!opt.gpgsm_program)
238     opt.gpgsm_program = gnupg_module_name (GNUPG_MODULE_NAME_GPGSM);
239
240   /* Run the selected command.  */
241   switch (cmd)
242     {
243     default:
244       interactive_loop ();
245       err = 0;
246       break;
247     }
248
249   if (err)
250     gnupg_status_printf (STATUS_FAILURE, "- %u", err);
251   else if (log_get_errorcount (0))
252     gnupg_status_printf (STATUS_FAILURE, "- %u", GPG_ERR_GENERAL);
253   else
254     gnupg_status_printf (STATUS_SUCCESS, NULL);
255   return log_get_errorcount (0)? 1:0;
256 }
257
258
259
260 /* Print all available information about the current card. */
261 static void
262 print_card_status (char *serialno, size_t serialnobuflen)
263 {
264   /* struct agent_card_info_s info; */
265   /* PKT_public_key *pk = xcalloc (1, sizeof *pk); */
266   /* kbnode_t keyblock = NULL; */
267   /* int rc; */
268   /* unsigned int uval; */
269   /* const unsigned char *thefpr; */
270   /* unsigned int thefprlen; */
271   /* int i; */
272
273   /* if (serialno && serialnobuflen) */
274   /*   *serialno = 0; */
275
276   /* rc = agent_scd_learn (&info, 0); */
277   /* if (rc) */
278   /*   { */
279   /*     if (opt.with_colons) */
280   /*       es_fputs ("AID:::\n", fp); */
281   /*     log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (rc)); */
282   /*     xfree (pk); */
283   /*     return; */
284   /*   } */
285
286   /* if (opt.with_colons) */
287   /*   es_fprintf (fp, "Reader:%s:", info.reader? info.reader : ""); */
288   /* else */
289   /*   tty_fprintf (fp, "Reader ...........: %s\n", */
290   /*                info.reader? info.reader : "[none]"); */
291   /* if (opt.with_colons) */
292   /*   es_fprintf (fp, "AID:%s:", info.serialno? info.serialno : ""); */
293   /* else */
294   /*   tty_fprintf (fp, "Application ID ...: %s\n", */
295   /*                info.serialno? info.serialno : "[none]"); */
296   /* if (!info.serialno || strncmp (info.serialno, "D27600012401", 12) */
297   /*     || strlen (info.serialno) != 32 ) */
298   /*   { */
299   /*     if (info.apptype && !strcmp (info.apptype, "NKS")) */
300   /*       { */
301   /*         if (opt.with_colons) */
302   /*           es_fputs ("netkey-card:\n", fp); */
303   /*         log_info ("this is a NetKey card\n"); */
304   /*       } */
305   /*     else if (info.apptype && !strcmp (info.apptype, "DINSIG")) */
306   /*       { */
307   /*         if (opt.with_colons) */
308   /*           es_fputs ("dinsig-card:\n", fp); */
309   /*         log_info ("this is a DINSIG compliant card\n"); */
310   /*       } */
311   /*     else if (info.apptype && !strcmp (info.apptype, "P15")) */
312   /*       { */
313   /*         if (opt.with_colons) */
314   /*           es_fputs ("pkcs15-card:\n", fp); */
315   /*         log_info ("this is a PKCS#15 compliant card\n"); */
316   /*       } */
317   /*     else if (info.apptype && !strcmp (info.apptype, "GELDKARTE")) */
318   /*       { */
319   /*         if (opt.with_colons) */
320   /*           es_fputs ("geldkarte-card:\n", fp); */
321   /*         log_info ("this is a Geldkarte compliant card\n"); */
322   /*       } */
323   /*     else */
324   /*       { */
325   /*         if (opt.with_colons) */
326   /*           es_fputs ("unknown:\n", fp); */
327   /*       } */
328   /*     log_info ("not an OpenPGP card\n"); */
329   /*     agent_release_card_info (&info); */
330   /*     xfree (pk); */
331   /*     return; */
332   /*   } */
333
334   /* if (!serialno) */
335   /*   ; */
336   /* else if (strlen (info.serialno)+1 > serialnobuflen) */
337   /*   log_error ("serial number longer than expected\n"); */
338   /* else */
339   /*   strcpy (serialno, info.serialno); */
340
341   /* if (opt.with_colons) */
342   /*   es_fputs ("openpgp-card:\n", fp); */
343
344
345   /*     tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n", */
346   /*                  info.serialno[12] == '0'?"":info.serialno+12, */
347   /*                  info.serialno[13], */
348   /*                  info.serialno[14] == '0'?"":info.serialno+14, */
349   /*                  info.serialno[15]); */
350   /*     tty_fprintf (fp, "Manufacturer .....: %s\n", */
351   /*                  get_manufacturer (xtoi_2(info.serialno+16)*256 */
352   /*                                    + xtoi_2 (info.serialno+18))); */
353   /*     tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20); */
354
355   /*     print_isoname (fp, "Name of cardholder: ", "name", info.disp_name); */
356   /*     print_name (fp, "Language prefs ...: ", info.disp_lang); */
357   /*     tty_fprintf (fp, "Salutation .......: %s\n", */
358   /*                  info.disp_sex == 1? _("Mr."): */
359   /*                  info.disp_sex == 2? _("Mrs.") : ""); */
360   /*     print_name (fp, "URL of public key : ", info.pubkey_url); */
361   /*     print_name (fp, "Login data .......: ", info.login_data); */
362   /*     if (info.private_do[0]) */
363   /*       print_name (fp, "Private DO 1 .....: ", info.private_do[0]); */
364   /*     if (info.private_do[1]) */
365   /*       print_name (fp, "Private DO 2 .....: ", info.private_do[1]); */
366   /*     if (info.private_do[2]) */
367   /*       print_name (fp, "Private DO 3 .....: ", info.private_do[2]); */
368   /*     if (info.private_do[3]) */
369   /*       print_name (fp, "Private DO 4 .....: ", info.private_do[3]); */
370   /*     if (info.cafpr1len) */
371   /*       { */
372   /*         tty_fprintf (fp, "CA fingerprint %d .:", 1); */
373   /*         print_shax_fpr (fp, info.cafpr1, info.cafpr1len); */
374   /*       } */
375   /*     if (info.cafpr2len) */
376   /*       { */
377   /*         tty_fprintf (fp, "CA fingerprint %d .:", 2); */
378   /*         print_shax_fpr (fp, info.cafpr2, info.cafpr2len); */
379   /*       } */
380   /*     if (info.cafpr3len) */
381   /*       { */
382   /*         tty_fprintf (fp, "CA fingerprint %d .:", 3); */
383   /*         print_shax_fpr (fp, info.cafpr3, info.cafpr3len); */
384   /*       } */
385   /*     tty_fprintf (fp,    "Signature PIN ....: %s\n", */
386   /*                  info.chv1_cached? _("not forced"): _("forced")); */
387   /*     if (info.key_attr[0].algo) */
388   /*       { */
389   /*         tty_fprintf (fp,    "Key attributes ...:"); */
390   /*         for (i=0; i < DIM (info.key_attr); i++) */
391   /*           if (info.key_attr[i].algo == PUBKEY_ALGO_RSA) */
392   /*             tty_fprintf (fp, " rsa%u", info.key_attr[i].nbits); */
393   /*           else if (info.key_attr[i].algo == PUBKEY_ALGO_ECDH */
394   /*                    || info.key_attr[i].algo == PUBKEY_ALGO_ECDSA */
395   /*                    || info.key_attr[i].algo == PUBKEY_ALGO_EDDSA) */
396   /*             { */
397   /*               const char *curve_for_print = "?"; */
398
399   /*               if (info.key_attr[i].curve) */
400   /*                 { */
401   /*                   const char *oid; */
402   /*                   oid = openpgp_curve_to_oid (info.key_attr[i].curve, NULL); */
403   /*                   if (oid) */
404   /*                     curve_for_print = openpgp_oid_to_curve (oid, 0); */
405   /*                 } */
406   /*               tty_fprintf (fp, " %s", curve_for_print); */
407   /*             } */
408   /*         tty_fprintf (fp, "\n"); */
409   /*       } */
410   /*     tty_fprintf (fp,    "Max. PIN lengths .: %d %d %d\n", */
411   /*                  info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]); */
412   /*     tty_fprintf (fp,    "PIN retry counter : %d %d %d\n", */
413   /*                  info.chvretry[0], info.chvretry[1], info.chvretry[2]); */
414   /*     tty_fprintf (fp,    "Signature counter : %lu\n", info.sig_counter); */
415   /*     if (info.extcap.kdf) */
416   /*       { */
417   /*         tty_fprintf (fp, "KDF setting ......: %s\n", */
418   /*                      info.kdf_do_enabled ? "on" : "off"); */
419   /*       } */
420   /*     if (info.extcap.bt) */
421   /*       { */
422   /*         tty_fprintf (fp, "UIF setting ......: Sign=%s Decrypt=%s Auth=%s\n", */
423   /*                      info.uif[0] ? "on" : "off", info.uif[1] ? "on" : "off", */
424   /*                      info.uif[2] ? "on" : "off"); */
425   /*       } */
426   /*     tty_fprintf (fp, "Signature key ....:"); */
427   /*     print_shax_fpr (fp, info.fpr1len? info.fpr1:NULL, info.fpr1len); */
428   /*     if (info.fpr1len && info.fpr1time) */
429   /*       { */
430   /*         tty_fprintf (fp, "      created ....: %s\n", */
431   /*                      isotimestamp (info.fpr1time)); */
432   /*         print_keygrip (fp, info.grp1); */
433   /*       } */
434   /*     tty_fprintf (fp, "Encryption key....:"); */
435   /*     print_shax_fpr (fp, info.fpr2len? info.fpr2:NULL, info.fpr2len); */
436   /*     if (info.fpr2len && info.fpr2time) */
437   /*       { */
438   /*         tty_fprintf (fp, "      created ....: %s\n", */
439   /*                      isotimestamp (info.fpr2time)); */
440   /*         print_keygrip (fp, info.grp2); */
441   /*       } */
442   /*     tty_fprintf (fp, "Authentication key:"); */
443   /*     print_shax_fpr (fp, info.fpr3len? info.fpr3:NULL, info.fpr3len); */
444   /*     if (info.fpr3len && info.fpr3time) */
445   /*       { */
446   /*         tty_fprintf (fp, "      created ....: %s\n", */
447   /*                      isotimestamp (info.fpr3time)); */
448   /*         print_keygrip (fp, info.grp3); */
449   /*       } */
450   /*     tty_fprintf (fp, "General key info..: "); */
451
452   /*     thefpr = (info.fpr1len? info.fpr1 : info.fpr2len? info.fpr2 : */
453   /*               info.fpr3len? info.fpr3 : NULL); */
454   /*     thefprlen = (info.fpr1len? info.fpr1len : info.fpr2len? info.fpr2len : */
455   /*                  info.fpr3len? info.fpr3len : 0); */
456   /*     /\* If the fingerprint is all 0xff, the key has no associated */
457   /*        OpenPGP certificate.  *\/ */
458   /*     if ( thefpr && !fpr_is_ff (thefpr, thefprlen) */
459   /*          && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen)) */
460   /*       { */
461   /*         print_pubkey_info (ctrl, fp, pk); */
462   /*         if (keyblock) */
463   /*           print_card_key_info (fp, keyblock); */
464   /*       } */
465   /*     else */
466   /*       tty_fprintf (fp, "[none]\n"); */
467
468   /* release_kbnode (keyblock); */
469   /* free_public_key (pk); */
470   /* agent_release_card_info (&info); */
471 }
472
473
474 \f
475 static void
476 cmd_verify (void)
477 {
478   /* agent_scd_checkpin (serialnobuf); */
479 }
480
481
482 static void
483 cmd_name (void)
484 {
485   /* change_name (); */
486 }
487
488
489 static void
490 cmd_url (void)
491 {
492   /* change_url (); */
493 }
494
495
496 static void
497 cmd_fetch (void)
498 {
499   /* fetch_url (); */
500 }
501
502
503 static void
504 cmd_login (char *arg_string)
505 {
506   /* change_login (arg_string); */
507 }
508
509
510 static void
511 cmd_lang (void)
512 {
513   /* change_lang (); */
514 }
515
516
517 static void
518 cmd_salut (void)
519 {
520   /* change_salut (); */
521 }
522
523
524 static void
525 cmd_cafpr (int arg_number)
526 {
527   if ( arg_number < 1 || arg_number > 3 )
528     tty_printf ("usage: cafpr N\n"
529                 "       1 <= N <= 3\n");
530   /* else */
531   /*   change_cafpr (arg_number); */
532 }
533
534
535 static void
536 cmd_privatedo (int arg_number, char *arg_string)
537 {
538   if ( arg_number < 1 || arg_number > 4 )
539     tty_printf ("usage: privatedo N\n"
540                 "       1 <= N <= 4\n");
541   /* else */
542   /*   change_private_do (arg_string, arg_number); */
543 }
544
545
546 static void
547 cmd_writecert (int arg_number, char *arg_rest)
548 {
549   if ( arg_number != 3 )
550     tty_printf ("usage: writecert 3 < FILE\n");
551   /* else */
552   /*   change_cert (arg_rest); */
553 }
554
555
556 static void
557 cmd_readcert (int arg_number, char *arg_rest)
558 {
559   if ( arg_number != 3 )
560     tty_printf ("usage: readcert 3 > FILE\n");
561   /* else */
562   /*   read_cert (arg_rest); */
563 }
564
565
566 static void
567 cmd_forcesig (void)
568 {
569   /* toggle_forcesig (); */
570 }
571
572
573 static void
574 cmd_generate (void)
575 {
576   /* generate_card_keys (); */
577 }
578
579
580 static void
581 cmd_passwd (int allow_admin)
582 {
583   /* change_pin (0, allow_admin); */
584 }
585
586
587 static void
588 cmd_unblock (int allow_admin)
589 {
590   /* change_pin (1, allow_admin); */
591 }
592
593
594 static void
595 cmd_factoryreset (void)
596 {
597   /* factory_reset (); */
598 }
599
600
601 static void
602 cmd_kdfsetup (char *argstring)
603 {
604   /* kdf_setup (arg_string); */
605 }
606
607
608 static void
609 cmd_keyattr (void)
610 {
611   /* key_attr (); */
612 }
613
614
615 static void
616 cmd_uif (int arg_number, char *arg_rest)
617 {
618   if ( arg_number < 1 || arg_number > 3 )
619     tty_printf ("usage: uif N [on|off|permanent]\n"
620                 "       1 <= N <= 3\n");
621   /* else */
622   /*   uif (arg_number, arg_rest); */
623 }
624
625
626 \f
627 /* Data used by the command parser.  This needs to be outside of the
628  * function scope to allow readline based command completion.  */
629 enum cmdids
630   {
631     cmdNOP = 0,
632     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
633     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
634     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
635     cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
636     cmdKEYATTR, cmdUIF,
637     cmdINVCMD
638   };
639
640 static struct
641 {
642   const char *name;
643   enum cmdids id;
644   int admin_only;
645   const char *desc;
646 } cmds[] = {
647   { "quit"    , cmdQUIT  , 0, N_("quit this menu")},
648   { "q"       , cmdQUIT  , 0, NULL },
649   { "admin"   , cmdADMIN , 0, N_("show admin commands")},
650   { "help"    , cmdHELP  , 0, N_("show this help")},
651   { "?"       , cmdHELP  , 0, NULL },
652   { "list"    , cmdLIST  , 0, N_("list all available data")},
653   { "l"       , cmdLIST  , 0, NULL },
654   { "debug"   , cmdDEBUG , 0, NULL },
655   { "name"    , cmdNAME  , 1, N_("change card holder's name")},
656   { "url"     , cmdURL   , 1, N_("change URL to retrieve key")},
657   { "fetch"   , cmdFETCH , 0, N_("fetch the key specified in the card URL")},
658   { "login"   , cmdLOGIN , 1, N_("change the login name")},
659   { "lang"    , cmdLANG  , 1, N_("change the language preferences")},
660   { "salutation",cmdSALUT, 1, N_("change card holder's salutation")},
661   { "cafpr"   , cmdCAFPR , 1, N_("change a CA fingerprint")},
662   { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")},
663   { "generate", cmdGENERATE, 1, N_("generate new keys")},
664   { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
665   { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
666   { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code")},
667   { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
668   { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
669   { "key-attr", cmdKEYATTR, 1, N_("change the key attribute")},
670   { "uif", cmdUIF, 1, N_("change the User Interaction Flag")},
671   /* Note, that we do not announce these command yet. */
672   { "privatedo", cmdPRIVATEDO, 0, NULL },
673   { "readcert", cmdREADCERT, 0, NULL },
674   { "writecert", cmdWRITECERT, 1, NULL },
675   { NULL, cmdINVCMD, 0, NULL }
676 };
677
678
679 /* The main loop.  */
680 static void
681 interactive_loop (void)
682 {
683   char *answer = NULL;         /* The input line.  */
684   enum cmdids cmd = cmdNOP;    /* The command.  */
685   int cmd_admin_only;          /* The command is an admin only command.  */
686   int arg_number;              /* The first argument as a number.  */
687   char *arg_string = "";       /* The first argument as a string.  */
688   char *arg_rest = "";         /* The remaining arguments.  */
689   int redisplay = 1;           /* Whether to redisplay the main info.  */
690   int allow_admin = 0;         /* Whether admin commands are allowed.  */
691   char serialnobuf[50];
692   char *p;
693   int i;
694
695   for (;;)
696     {
697
698       tty_printf ("\n");
699       if (redisplay)
700         {
701           print_card_status (serialnobuf, DIM (serialnobuf));
702           tty_printf("\n");
703           redisplay = 0;
704         }
705
706       do
707         {
708           xfree (answer);
709           tty_enable_completion (command_completion);
710           answer = tty_get (_("gpg/card> "));
711           tty_kill_prompt();
712           tty_disable_completion ();
713           trim_spaces(answer);
714         }
715       while ( *answer == '#' );
716
717       arg_number = 0;
718       cmd_admin_only = 0;
719       if (!*answer)
720         cmd = cmdLIST; /* We default to the list command */
721       else if (*answer == CONTROL_D)
722         cmd = cmdQUIT;
723       else
724         {
725           if ((p=strchr (answer,' ')))
726             {
727               *p++ = 0;
728               trim_spaces (answer);
729               trim_spaces (p);
730               arg_number = atoi (p);
731               arg_string = p;
732               arg_rest = p;
733               while (digitp (arg_rest))
734                 arg_rest++;
735               while (spacep (arg_rest))
736                 arg_rest++;
737             }
738
739           for (i=0; cmds[i].name; i++ )
740             if (!ascii_strcasecmp (answer, cmds[i].name ))
741               break;
742
743           cmd = cmds[i].id;
744           cmd_admin_only = cmds[i].admin_only;
745         }
746
747       if (!allow_admin && cmd_admin_only)
748         {
749           tty_printf ("\n");
750           tty_printf (_("Admin-only command\n"));
751           continue;
752         }
753
754       switch (cmd)
755         {
756         case cmdNOP:
757           break;
758
759         case cmdQUIT:
760           goto leave;
761
762         case cmdHELP:
763           for (i=0; cmds[i].name; i++ )
764             if(cmds[i].desc
765                && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
766               tty_printf("%-14s %s\n", cmds[i].name, _(cmds[i].desc) );
767           break;
768
769         case cmdADMIN:
770           if ( !strcmp (arg_string, "on") )
771             allow_admin = 1;
772           else if ( !strcmp (arg_string, "off") )
773             allow_admin = 0;
774           else if ( !strcmp (arg_string, "verify") )
775             {
776               /* Force verification of the Admin Command.  However,
777                  this is only done if the retry counter is at initial
778                  state.  */
779               /* FIXME: Must depend on the type of the card.  */
780               /* char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1); */
781               /* strcpy (stpcpy (tmp, serialnobuf), "[CHV3]"); */
782               /* allow_admin = !agent_scd_checkpin (tmp); */
783               /* xfree (tmp); */
784             }
785           else /* Toggle. */
786             allow_admin=!allow_admin;
787           if(allow_admin)
788             tty_printf(_("Admin commands are allowed\n"));
789           else
790             tty_printf(_("Admin commands are not allowed\n"));
791           break;
792
793         case cmdVERIFY:    cmd_verify (); redisplay = 1; break;
794         case cmdLIST:                     redisplay = 1; break;
795         case cmdNAME:      cmd_name ();   break;
796         case cmdURL:       cmd_url ();    break;
797         case cmdFETCH:     cmd_fetch ();  break;
798         case cmdLOGIN:     cmd_login (arg_string); break;
799         case cmdLANG:      cmd_lang ();   break;
800         case cmdSALUT:     cmd_salut ();  break;
801         case cmdCAFPR:     cmd_cafpr (arg_number); break;
802         case cmdPRIVATEDO: cmd_privatedo (arg_number, arg_string); break;
803         case cmdWRITECERT: cmd_writecert (arg_number, arg_rest); break;
804         case cmdREADCERT:  cmd_readcert (arg_number, arg_rest); break;
805         case cmdFORCESIG:  cmd_forcesig (); break;
806         case cmdGENERATE:  cmd_generate (); break;
807         case cmdPASSWD:    cmd_passwd (allow_admin); break;
808         case cmdUNBLOCK:   cmd_unblock (allow_admin); break;
809         case cmdFACTORYRESET: cmd_factoryreset (); break;
810         case cmdKDFSETUP:  cmd_kdfsetup (arg_string); break;
811         case cmdKEYATTR:   cmd_keyattr (); break;
812         case cmdUIF:       cmd_uif (arg_number, arg_rest); break;
813
814         case cmdINVCMD:
815         default:
816           tty_printf ("\n");
817           tty_printf (_("Invalid command  (try \"help\")\n"));
818           break;
819         } /* End command switch. */
820     } /* End of main menu loop. */
821
822  leave:
823   xfree (answer);
824 }
825
826 #ifdef HAVE_LIBREADLINE
827 /* Helper function for readline's command completion. */
828 static char *
829 command_generator (const char *text, int state)
830 {
831   static int list_index, len;
832   const char *name;
833
834   /* If this is a new word to complete, initialize now.  This includes
835    * saving the length of TEXT for efficiency, and initializing the
836    index variable to 0. */
837   if (!state)
838     {
839       list_index = 0;
840       len = strlen(text);
841     }
842
843   /* Return the next partial match */
844   while ((name = cmds[list_index].name))
845     {
846       /* Only complete commands that have help text. */
847       if (cmds[list_index++].desc && !strncmp (name, text, len))
848         return strdup(name);
849     }
850
851   return NULL;
852 }
853
854 /* Second helper function for readline's command completion.  */
855 static char **
856 command_completion (const char *text, int start, int end)
857 {
858   (void)end;
859
860   /* If we are at the start of a line, we try and command-complete.
861    * If not, just do nothing for now. */
862   if (!start)
863     return rl_completion_matches (text, command_generator);
864
865   rl_attempted_completion_over = 1;
866
867   return NULL;
868 }
869 #endif /*HAVE_LIBREADLINE*/