core: Make sure FD_SET is not used with an out of range fd.
[gpgme.git] / src / gpgme-tool.c
index be8ed07..557ed64 100644 (file)
 #ifdef HAVE_LOCALE_H
 #include <locale.h>
 #endif
-#ifdef HAVE_ARGP_H
-#include <argp.h>
-#endif
 
 #include <assuan.h>
 
+#include "argparse.h"
 #include "gpgme.h"
 
 /* GCC attributes.  */
 
 
 \f
-#ifndef HAVE_ARGP_H
-/* Minimal argp implementation.  */
-
-/* Differences to ARGP:
-   argp_program_version: Required.
-   argp_program_bug_address: Required.
-   argp_program_version_hook: Not supported.
-   argp_err_exit_status: Required.
-   struct argp: Children and help_filter not supported.
-   argp_domain: Not supported.
-   struct argp_option: Group not supported.  Options are printed in
-   order given.  Flags OPTION_ALIAS, OPTION_DOC and OPTION_NO_USAGE
-   are not supported.
-   argp_parse: No flags are supported (ARGP_PARSE_ARGV0, ARGP_NO_ERRS,
-   ARGP_NO_ARGS, ARGP_IN_ORDER, ARGP_NO_HELP, ARGP_NO_EXIT,
-   ARGP_LONG_ONLY, ARGP_SILENT).  ARGP must not be NULL.
-   argp_help: Flag ARGP_HELP_LONG_ONLY not supported.
-   argp_state: argc, argv, next may not be modified and should not be used.  */
-
-extern const char *argp_program_version;
-extern const char *argp_program_bug_address;
-extern error_t argp_err_exit_status;
-
-struct argp_option
-{
-  const char *name;
-  int key;
-  const char *arg;
-#define OPTION_ARG_OPTIONAL 0x1
-#define OPTION_HIDDEN 0x2
-  int flags;
-  const char *doc;
-  int group;
-};
-
-struct argp;
-struct argp_state
-{
-  const struct argp *const root_argp;
-  int argc;
-  char **argv;
-  int next;
-  unsigned flags;
-  unsigned arg_num;
-  int quoted;
-  void *input;
-  void **child_inputs;
-  void *hook;
-  char *name;
-  FILE *err_stream;
-  FILE *out_stream;
-  void *pstate;
-};
-
-#ifdef EDEADLK
-# define ARGP_ERR_UNKNOWN EDEADLK /* POSIX */
-#else
-# define ARGP_ERR_UNKNOWN EDEADLOCK /* *GNU/kFreebsd does not define this) */
-#endif
-#define ARGP_KEY_ARG 0
-#define ARGP_KEY_ARGS 0x1000006
-#define ARGP_KEY_END 0x1000001
-#define ARGP_KEY_NO_ARGS 0x1000002
-#define ARGP_KEY_INIT 0x1000003
-#define ARGP_KEY_FINI 0x1000007
-#define ARGP_KEY_SUCCESS 0x1000004
-#define ARGP_KEY_ERROR 0x1000005
-typedef error_t (*argp_parser_t) (int key, char *arg, struct argp_state *state);
-
-struct argp
-{
-  const struct argp_option *options;
-  argp_parser_t parser;
-  const char *args_doc;
-  const char *doc;
-
-  const struct argp_child *children;
-  char *(*help_filter) (int key, const char *text, void *input);
-  const char *argp_domain;
-};
-
-#define ARGP_HELP_USAGE ARGP_HELP_SHORT_USAGE
-#define ARGP_HELP_SHORT_USAGE 0x02
-#define ARGP_HELP_SEE 0x04
-#define ARGP_HELP_LONG 0x08
-#define ARGP_HELP_PRE_DOC 0x10
-#define ARGP_HELP_POST_DOC 0x20
-#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC)
-#define ARGP_HELP_BUG_ADDR 0x40
-#define ARGP_HELP_EXIT_ERR 0x100
-#define ARGP_HELP_EXIT_OK 0x200
-#define ARGP_HELP_STD_ERR (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
-#define ARGP_HELP_STD_USAGE \
-  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR)
-#define ARGP_HELP_STD_HELP \
-  (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK  \
-   | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR)
-
-
-void argp_error (const struct argp_state *state,
-                 const char *fmt, ...) GT_GCC_A_PRINTF(2, 3);
-
-
-
-char *
-_argp_pname (char *name)
-{
-  char *pname = name;
-  char *bname = strrchr (pname, '/');
-  if (! bname)
-    bname = strrchr (pname, '\\');
-  if (bname)
-    pname = bname + 1;
-  return pname;
-}
-
-
-void
-_argp_state_help (const struct argp *argp, const struct argp_state *state,
-                 FILE *stream, unsigned flags, char *name)
-{
-  if (state)
-    name = state->name;
-
-  if (flags & ARGP_HELP_SHORT_USAGE)
-    fprintf (stream, "Usage: %s [OPTIONS...] %s\n", name, argp->args_doc);
-  if (flags & ARGP_HELP_SEE)
-    fprintf (stream, "Try `%s --help' or `%s --usage' for more information.\n",
-            name, name);
-  if (flags & ARGP_HELP_PRE_DOC)
-    {
-      char buf[1024];
-      char *end;
-      strncpy (buf, argp->doc, sizeof (buf));
-      buf[sizeof (buf) - 1] = '\0';
-      end = strchr (buf, '\v');
-      if (end)
-       *end = '\0';
-      fprintf (stream, "%s\n%s", buf, buf[0] ? "\n" : "");
-    }
-  if (flags & ARGP_HELP_LONG)
-    {
-      const struct argp_option *opt = argp->options;
-      while (opt->key)
-       {
-         #define NSPACES 29
-         char spaces[NSPACES + 1] = "                              ";
-         int len = 0;
-         fprintf (stream, "  ");
-         len += 2;
-         if (isascii (opt->key))
-           {
-             fprintf (stream, "-%c", opt->key);
-             len += 2;
-             if (opt->name)
-               {
-                 fprintf (stream, ", ");
-                 len += 2;
-               }
-           }
-         if (opt->name)
-           {
-             fprintf (stream, "--%s", opt->name);
-             len += 2 + strlen (opt->name);
-           }
-         if (opt->arg && (opt->flags & OPTION_ARG_OPTIONAL))
-           {
-             fprintf (stream, "[=%s]", opt->arg);
-             len += 3 + strlen (opt->arg);
-           }
-         else if (opt->arg)
-           {
-             fprintf (stream, "=%s", opt->arg);
-             len += 1 + strlen (opt->arg);
-           }
-         if (len >= NSPACES)
-           len = NSPACES - 1;
-         spaces[NSPACES - len] = '\0';
-         fprintf (stream, "%s%s\n", spaces, opt->doc);
-         opt++;
-       }
-      fprintf (stream, "  -?, --help                 Give this help list\n");
-      fprintf (stream, "      --usage                Give a short usage "
-              "message\n");
-    }
-  if (flags & ARGP_HELP_POST_DOC)
-    {
-      char buf[1024];
-      char *end;
-      strncpy (buf, argp->doc, sizeof (buf));
-      buf[sizeof (buf) - 1] = '\0';
-      end = strchr (buf, '\v');
-      if (end)
-       {
-         end++;
-         if (*end)
-           fprintf (stream, "\n%s\n", end);
-       }
-      fprintf (stream, "\nMandatory or optional arguments to long options are also mandatory or optional\n");
-      fprintf (stream, "for any corresponding short options.\n");
-    }
-  if (flags & ARGP_HELP_BUG_ADDR)
-    fprintf (stream, "\nReport bugs to %s.\n", argp_program_bug_address);
-
-  if (flags & ARGP_HELP_EXIT_ERR)
-    exit (argp_err_exit_status);
-  if (flags & ARGP_HELP_EXIT_OK)
-    exit (0);
-}
-
-
-void
-argp_usage (const struct argp_state *state)
-{
-  _argp_state_help (state->root_argp, state, state->err_stream,
-                   ARGP_HELP_STD_USAGE, state->name);
-}
-
-
-void
-argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags)
-{
-  _argp_state_help (state->root_argp, state, stream, flags, state->name);
-}
-
-
-void
-argp_error (const struct argp_state *state, const char *fmt, ...)
-{
-  va_list ap;
-
-  fprintf (state->err_stream, "%s: ", state->name);
-  va_start (ap, fmt);
-  vfprintf (state->err_stream, fmt, ap);
-  va_end (ap);
-  fprintf (state->err_stream, "\n");
-  argp_state_help (state, state->err_stream, ARGP_HELP_STD_ERR);
-  exit (argp_err_exit_status);
-}
-
-
-void
-argp_help (const struct argp *argp, FILE *stream, unsigned flags, char *name)
-{
-  _argp_state_help (argp, NULL, stream, flags, name);
-}
-
-
-error_t
-argp_parse (const struct argp *argp, int argc,
-           char **argv, unsigned flags, int *arg_index, void *input)
-{
-  int rc = 0;
-  struct argp_state state = { argp, argc, argv, 1, flags, 0, 0, input,
-                             NULL, NULL, _argp_pname (argv[0]),
-                             stderr, stdout, NULL };
-  /* All non-option arguments are collected at the beginning of
-     &argv[1] during processing.  This is a counter for their number.  */
-  int non_opt_args = 0;
-
-  rc = argp->parser (ARGP_KEY_INIT, NULL, &state);
-  if (rc && rc != ARGP_ERR_UNKNOWN)
-    goto argperror;
-
-  while (state.next < state.argc - non_opt_args)
-    {
-      int idx = state.next;
-      state.next++;
-
-      if (! strcasecmp (state.argv[idx], "--"))
-       {
-         state.quoted = idx;
-         continue;
-       }
-
-      if (state.quoted || state.argv[idx][0] != '-')
-       {
-         char *arg_saved = state.argv[idx];
-         non_opt_args++;
-         memmove (&state.argv[idx], &state.argv[idx + 1],
-                  (state.argc - 1 - idx) * sizeof (char *));
-         state.argv[argc - 1] = arg_saved;
-         state.next--;
-       }
-      else if (! strcasecmp (state.argv[idx], "--help")
-              || !strcmp (state.argv[idx], "-?"))
-       {
-         argp_state_help (&state, state.out_stream, ARGP_HELP_STD_HELP);
-       }
-      else if (! strcasecmp (state.argv[idx], "--usage"))
-       {
-         argp_state_help (&state, state.out_stream,
-                          ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK);
-       }
-      else if (! strcasecmp (state.argv[idx], "--version")
-              || !strcmp (state.argv[idx], "-V"))
-       {
-         fprintf (state.out_stream, "%s\n", argp_program_version);
-         exit (0);
-       }
-      else
-       {
-         /* Search for option and call parser with its KEY.  */
-         int key = ARGP_KEY_ARG; /* Just some dummy value.  */
-         const struct argp_option *opt = argp->options;
-         char *arg = NULL;
-         int found = 0;
-
-         /* Check for --opt=value syntax.  */
-         arg = strchr (state.argv[idx], '=');
-         if (arg)
-           {
-             *arg = '\0';
-             arg++;
-           }
-
-         if (state.argv[idx][1] != '-')
-           key = state.argv[idx][1];
-
-         while (! found && opt->key)
-           {
-             if (key == opt->key
-                 || (key == ARGP_KEY_ARG
-                     && ! strcasecmp (&state.argv[idx][2], opt->name)))
-               {
-                 if (arg && !opt->arg)
-                   argp_error (&state, "Option %s does not take an argument",
-                               state.argv[idx]);
-                 if (opt->arg && state.next < state.argc
-                     && state.argv[idx + 1][0] != '-')
-                   {
-                     arg = state.argv[idx + 1];
-                     state.next++;
-                   }
-                 if (opt->arg && !(opt->flags & OPTION_ARG_OPTIONAL))
-                   argp_error (&state, "Option %s requires an argument",
-                               state.argv[idx]);
-
-                 rc = argp->parser (opt->key, arg, &state);
-                 if (rc == ARGP_ERR_UNKNOWN)
-                   break;
-                 else if (rc)
-                   goto argperror;
-                 found = 1;
-               }
-             opt++;
-           }
-         if (! found)
-           argp_error (&state, "Unknown option %s", state.argv[idx]);
-       }
-    }
-
-  while (state.next < state.argc)
-    {
-      /* Call parser for all non-option args.  */
-      int idx = state.next;
-      state.next++;
-      rc = argp->parser (ARGP_KEY_ARG, state.argv[idx], &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-      if (rc == ARGP_ERR_UNKNOWN)
-       {
-         int old_next = state.next;
-         rc = argp->parser (ARGP_KEY_ARGS, NULL, &state);
-         if (rc == ARGP_ERR_UNKNOWN)
-           {
-             argp_error (&state, "Too many arguments");
-             goto argperror;
-           }
-         if (! rc && state.next == old_next)
-           {
-             state.arg_num += state.argc - state.next;
-             state.next = state.argc;
-           }
-       }
-      else
-       state.arg_num++;
-    }
-
-  if (state.arg_num == 0)
-    {
-      rc = argp->parser (ARGP_KEY_NO_ARGS, NULL, &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-    }
-  if (state.next == state.argc)
-    {
-      rc = argp->parser (ARGP_KEY_END, NULL, &state);
-      if (rc && rc != ARGP_ERR_UNKNOWN)
-       goto argperror;
-    }
-  rc = argp->parser (ARGP_KEY_FINI, NULL, &state);
-  if (rc && rc != ARGP_ERR_UNKNOWN)
-    goto argperror;
-
-  rc = 0;
-  argp->parser (ARGP_KEY_SUCCESS, NULL, &state);
-
- argperror:
-  if (rc)
-    {
-      argp_error (&state, "unexpected error: %s", strerror (rc));
-      argp->parser (ARGP_KEY_ERROR, NULL, &state);
-    }
-
-  argp->parser (ARGP_KEY_FINI, NULL, &state);
-
-  if (arg_index)
-    *arg_index = state.next - 1;
-
-  return 0;
-}
-#endif
-
-\f
 /* MEMBUF */
 
 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
@@ -831,14 +414,17 @@ result_xml_escape (const char *data, char **buf)
   membuf_t mb;
 
   init_membuf (&mb, 128);
-  data_len = strlen (data);
-  for (i = 0; i < data_len; i++)
+  if (data)
     {
-      r = result_xml_escape_replacement (data[i]);
-      if (r)
-        put_membuf (&mb, r, strlen (r));
-      else
-        put_membuf (&mb, data+i, 1);
+      data_len = strlen (data);
+      for (i = 0; i < data_len; i++)
+        {
+          r = result_xml_escape_replacement (data[i]);
+          if (r)
+            put_membuf (&mb, r, strlen (r));
+          else
+            put_membuf (&mb, data+i, 1);
+        }
     }
   put_membuf (&mb, "", 1);
   *buf = get_membuf (&mb, NULL);
@@ -1861,6 +1447,8 @@ gt_get_keylist_mode (gpgme_tool_t gt)
     modes[idx++] = "sigs";
   if (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)
     modes[idx++] = "sig_notations";
+  if (mode & GPGME_KEYLIST_MODE_WITH_SECRET)
+    modes[idx++] = "with_secret";
   if (mode & GPGME_KEYLIST_MODE_EPHEMERAL)
     modes[idx++] = "ephemeral";
   if (mode & GPGME_KEYLIST_MODE_VALIDATE)
@@ -2591,6 +2179,8 @@ cmd_keylist_mode (assuan_context_t ctx, char *line)
        mode |= GPGME_KEYLIST_MODE_SIGS;
       if (strstr (line, "sig_notations"))
        mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
+      if (strstr (line, "with_secret"))
+       mode |= GPGME_KEYLIST_MODE_WITH_SECRET;
       if (strstr (line, "ephemeral"))
        mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
       if (strstr (line, "validate"))
@@ -3047,7 +2637,7 @@ cmd_import (assuan_context_t ctx, char *line)
 
 
 static const char hlp_export[] =
-  "EXPORT [--extern] [--minimal] [<pattern>]\n"
+  "EXPORT [--extern] [--minimal] [--secret [--pkcs12] [--raw]] [<pattern>]\n"
   "\n"
   "Export the keys described by PATTERN.  Write the\n"
   "the output to the object set by the last OUTPUT command.";
@@ -3075,6 +2665,12 @@ cmd_export (assuan_context_t ctx, char *line)
     mode |= GPGME_EXPORT_MODE_EXTERN;
   if (has_option (line, "--minimal"))
     mode |= GPGME_EXPORT_MODE_MINIMAL;
+  if (has_option (line, "--secret"))
+    mode |= GPGME_EXPORT_MODE_SECRET;
+  if (has_option (line, "--raw"))
+    mode |= GPGME_EXPORT_MODE_RAW;
+  if (has_option (line, "--pkcs12"))
+    mode |= GPGME_EXPORT_MODE_PKCS12;
 
   line = skip_options (line);
 
@@ -3298,7 +2894,15 @@ cmd_keylist (assuan_context_t ctx, char *line)
          while (subkey) {
            result_xml_tag_start (&state, "subkey", NULL);
            /* FIXME: more data */
-           result_add_fpr (&state, "fpr", subkey->fpr);
+           result_add_keyid (&state, "keyid", subkey->keyid);
+            if (subkey->fpr)
+              result_add_fpr (&state, "fpr", subkey->fpr);
+            result_add_value (&state, "secret", subkey->secret);
+            result_add_value (&state, "is_cardkey", subkey->is_cardkey);
+            if (subkey->card_number)
+              result_add_string (&state, "card_number", subkey->card_number);
+            if (subkey->curve)
+              result_add_string (&state, "curve", subkey->curve);
            result_xml_tag_end (&state);  /* subkey */
            subkey = subkey->next;
          }
@@ -3701,77 +3305,50 @@ gpgme_server (gpgme_tool_t gt)
 
 
 \f
-/* MAIN PROGRAM STARTS HERE.  */
-
-const char *argp_program_version = VERSION;
-const char *argp_program_bug_address = "bug-gpgme@gnupg.org";
-error_t argp_err_exit_status = 1;
-
-static char doc[] = "GPGME Tool -- Assuan server exposing GPGME operations";
-static char args_doc[] = "COMMAND [OPTIONS...]";
-
-static struct argp_option options[] = {
-  { "server", 's', 0, 0, "Server mode" },
-  { "gpg-binary", 501, "FILE", 0, "Use FILE for the GPG backend" },
-  { 0 }
-};
-
-static error_t parse_options (int key, char *arg, struct argp_state *state);
-static struct argp argp = { options, parse_options, args_doc, doc };
-
-struct args
-{
-  enum { CMD_DEFAULT, CMD_SERVER } cmd;
-  const char *gpg_binary;
-};
-
-void
-args_init (struct args *args)
-{
-  memset (args, '\0', sizeof (*args));
-  args->cmd = CMD_DEFAULT;
-}
-
-
-static error_t
-parse_options (int key, char *arg, struct argp_state *state)
+static const char *
+my_strusage( int level )
 {
-  struct args *args = state->input;
+  const char *p;
 
-  switch (key)
+  switch (level)
     {
-    case 's':
-      args->cmd = CMD_SERVER;
-      break;
-
-    case 501:
-      args->gpg_binary = arg;
+    case 11: p = "gpgme-tool"; break;
+    case 13: p = PACKAGE_VERSION; break;
+    case 14: p = "Copyright (C) 2015 g10 Code GmbH"; break;
+    case 19: p = "Please report bugs to <" PACKAGE_BUGREPORT ">.\n"; break;
+    case 1:
+    case 40:
+      p = "Usage: gpgme-tool [OPTIONS] [COMMANDS]";
       break;
-#if 0
-    case ARGP_KEY_ARG:
-      if (state->arg_num >= 2)
-       argp_usage (state);
-      printf ("Arg[%i] = %s\n", state->arg_num, arg);
+    case 41:
+      p = "GPGME Tool -- Assuan server exposing GPGME operations\n";
       break;
-    case ARGP_KEY_END:
-      if (state->arg_num < 2)
-       argp_usage (state);
+    case 42:
+      p = "1"; /* Flag print 40 as part of 41. */
       break;
-#endif
-
-    default:
-      return ARGP_ERR_UNKNOWN;
+    default: p = NULL; break;
     }
-  return 0;
+  return p;
 }
 
-\f
+
 int
 main (int argc, char *argv[])
 {
-  struct args args;
+  static ARGPARSE_OPTS opts[] = {
+    ARGPARSE_c  ('s', "server",      "Server mode"),
+    ARGPARSE_s_s(501, "gpg-binary",  "|FILE|Use FILE for the GPG backend"),
+    ARGPARSE_c  (502, "lib-version", "Show library version"),
+    ARGPARSE_end()
+  };
+  ARGPARSE_ARGS pargs = { &argc, &argv, 0 };
+  enum { CMD_DEFAULT, CMD_SERVER, CMD_LIBVERSION } cmd = CMD_DEFAULT;
+  const char *gpg_binary = NULL;
   struct gpgme_tool gt;
   gpg_error_t err;
+  int needgt = 1;
+
+  set_strusage (my_strusage);
 
 #ifdef HAVE_SETLOCALE
   setlocale (LC_ALL, "");
@@ -3784,34 +3361,56 @@ main (int argc, char *argv[])
   gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
 #endif
 
-  args_init (&args);
-
-  argp_parse (&argp, argc, argv, 0, 0, &args);
   log_init ();
 
-  if (args.gpg_binary)
+  while (arg_parse  (&pargs, opts))
     {
-      if (access (args.gpg_binary, X_OK))
+      switch (pargs.r_opt)
+        {
+        case 's': cmd = CMD_SERVER; break;
+        case 501: gpg_binary = pargs.r.ret_str; break;
+        case 502: cmd = CMD_LIBVERSION; break;
+        default:
+          pargs.err = ARGPARSE_PRINT_WARNING;
+         break;
+        }
+    }
+
+  if (cmd == CMD_LIBVERSION)
+    needgt = 0;
+
+  if (needgt && gpg_binary)
+    {
+      if (access (gpg_binary, X_OK))
         err = gpg_error_from_syserror ();
       else
         err = gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP,
-                                     args.gpg_binary, NULL);
+                                     gpg_binary, NULL);
       if (err)
         log_error (1, err, "error witching OpenPGP engine to '%s'",
-                   args.gpg_binary);
+                   gpg_binary);
     }
 
-  gt_init (&gt);
+  if (needgt)
+    gt_init (&gt);
 
-  switch (args.cmd)
+  switch (cmd)
     {
     case CMD_DEFAULT:
     case CMD_SERVER:
       gpgme_server (&gt);
       break;
+
+    case CMD_LIBVERSION:
+      printf ("Version from header: %s (0x%06x)\n",
+              GPGME_VERSION, GPGME_VERSION_NUMBER);
+      printf ("Version from binary: %s\n", gpgme_check_version (NULL));
+      printf ("Copyright blurb ...:%s\n", gpgme_check_version ("\x01\x01"));
+      break;
     }
 
-  gpgme_release (gt.ctx);
+  if (needgt)
+    gpgme_release (gt.ctx);
 
 #ifdef HAVE_W32CE_SYSTEM
   /* Give the buggy ssh server time to flush the output buffers.  */
@@ -3820,4 +3419,3 @@ main (int argc, char *argv[])
 
   return 0;
 }
-