gpgme/src/gpgme-tool.c

2013 lines
44 KiB
C
Raw Normal View History

/* gpgme-tool.c - GnuPG Made Easy.
Copyright (C) 2000 Werner Koch (dd9jn)
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007 g10 Code GmbH
This file is part of GPGME.
GPGME is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.
GPGME is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA. */
#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <ctype.h>
#include <stdarg.h>
#include <locale.h>
#ifdef HAVE_ARGP_H
#include <argp.h>
#endif
#include "gpgme.h"
#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;
};
#define ARGP_ERR_UNKNOWN E2BIG
#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)
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", state.argv[idx]);
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
/* SUPPORT. */
FILE *log_stream;
char *program_name = "gpgme-tool";
void
log_init (void)
{
log_stream = stderr;
}
void
log_error (int status, gpg_error_t errnum, const char *fmt, ...)
{
va_list ap;
fprintf (log_stream, "%s: ", program_name);
va_start (ap, fmt);
vfprintf (log_stream, fmt, ap);
va_end (ap);
if (errnum)
fprintf (log_stream, ": %s <%s>", gpg_strerror (errnum),
gpg_strsource (errnum));
fprintf (log_stream, "\n");
if (status)
exit (status);
}
typedef enum status
{
STATUS_PROTOCOL,
STATUS_PROGRESS,
STATUS_ENGINE,
STATUS_ARMOR,
STATUS_TEXTMODE,
STATUS_INCLUDE_CERTS,
STATUS_KEYLIST_MODE,
STATUS_ENCRYPT_RESULT
} status_t;
const char *status_string[] =
{
"PROTOCOL",
"PROGRESS",
"ENGINE",
"ARMOR",
"TEXTMODE",
"INCLUDE_CERTS",
"KEYLIST_MODE",
"ENCRYPT_RESULT"
};
struct gpgme_tool
{
gpgme_ctx_t ctx;
#define MAX_RECIPIENTS 10
gpgme_key_t recipients[MAX_RECIPIENTS + 1];
int recipients_nr;
gpg_error_t (*write_status) (void *hook, const char *status, const char *msg);
void *write_status_hook;
};
typedef struct gpgme_tool *gpgme_tool_t;
/* Forward declaration. */
void gt_write_status (gpgme_tool_t gt, status_t status, ...);
void
_gt_progress_cb (void *opaque, const char *what,
int type, int current, int total)
{
gpgme_tool_t gt = opaque;
char buf[100];
snprintf (buf, sizeof (buf), "0x%02x %i %i", type, current, total);
gt_write_status (gt, STATUS_PROGRESS, what, buf);
}
gpg_error_t
_gt_gpgme_new (gpgme_tool_t gt, gpgme_ctx_t *ctx)
{
gpg_error_t err;
err = gpgme_new (ctx);
if (err)
return err;
gpgme_set_progress_cb (*ctx, _gt_progress_cb, gt);
return 0;
}
void
gt_init (gpgme_tool_t gt)
{
memset (gt, '\0', sizeof (*gt));
gpg_error_t err;
err = _gt_gpgme_new (gt, &gt->ctx);
if (err)
log_error (1, err, "can't create gpgme context");
}
gpg_error_t
gt_signers_add (gpgme_tool_t gt, const char *fpr)
{
gpg_error_t err;
gpgme_key_t key;
err = gpgme_get_key (gt->ctx, fpr, &key, 0);
if (err)
return err;
return gpgme_signers_add (gt->ctx, key);
}
gpg_error_t
gt_signers_clear (gpgme_tool_t gt)
{
gpgme_signers_clear (gt->ctx);
return 0;
}
gpg_error_t
gt_recipients_add (gpgme_tool_t gt, const char *fpr)
{
gpg_error_t err;
gpgme_key_t key;
if (gt->recipients_nr >= MAX_RECIPIENTS)
return gpg_error_from_errno (ENOMEM);
err = gpgme_get_key (gt->ctx, fpr, &key, 0);
if (err)
return err;
gt->recipients[gt->recipients_nr++] = key;
return 0;
}
void
gt_recipients_clear (gpgme_tool_t gt)
{
int idx;
for (idx = 0; idx < gt->recipients_nr; idx++)
gpgme_key_unref (gt->recipients[idx]);
memset (gt->recipients, '\0', gt->recipients_nr * sizeof (gpgme_key_t));
gt->recipients_nr = 0;
}
gpg_error_t
gt_reset (gpgme_tool_t gt)
{
gpg_error_t err;
gpgme_ctx_t ctx;
err = _gt_gpgme_new (gt, &ctx);
if (err)
return err;
gpgme_release (gt->ctx);
gt->ctx = ctx;
gt_recipients_clear (gt);
return 0;
}
void
gt_write_status (gpgme_tool_t gt, status_t status, ...)
{
va_list ap;
const char *text;
char buf[950];
char *p;
size_t n;
gpg_error_t err;
va_start (ap, status);
p = buf;
n = 0;
while ((text = va_arg (ap, const char *)))
{
if (n)
{
*p++ = ' ';
n++;
}
while (*text && n < sizeof (buf) - 2)
{
*p++ = *text++;
n++;
}
}
*p = 0;
va_end (ap);
err = gt->write_status (gt->write_status_hook, status_string[status], buf);
if (err)
log_error (1, err, "can't write status line");
}
gpg_error_t
gt_get_engine_info (gpgme_tool_t gt, gpgme_protocol_t proto)
{
gpgme_engine_info_t info;
info = gpgme_ctx_get_engine_info (gt->ctx);
while (info)
{
if (proto == GPGME_PROTOCOL_UNKNOWN || proto == info->protocol)
gt_write_status (gt, STATUS_ENGINE,
gpgme_get_protocol_name (info->protocol),
info->file_name, info->version,
info->req_version, info->home_dir);
info = info->next;
}
return 0;
}
gpgme_protocol_t
gt_protocol_from_name (const char *name)
{
if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_OpenPGP)))
return GPGME_PROTOCOL_OpenPGP;
if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_CMS)))
return GPGME_PROTOCOL_CMS;
if (! strcasecmp (name,gpgme_get_protocol_name (GPGME_PROTOCOL_GPGCONF)))
return GPGME_PROTOCOL_GPGCONF;
if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_ASSUAN)))
return GPGME_PROTOCOL_ASSUAN;
if (! strcasecmp (name, gpgme_get_protocol_name (GPGME_PROTOCOL_G13)))
return GPGME_PROTOCOL_G13;
return GPGME_PROTOCOL_UNKNOWN;
}
gpg_error_t
gt_set_protocol (gpgme_tool_t gt, gpgme_protocol_t proto)
{
return gpgme_set_protocol (gt->ctx, proto);
}
gpg_error_t
gt_get_protocol (gpgme_tool_t gt)
{
gpgme_protocol_t proto = gpgme_get_protocol (gt->ctx);
gt_write_status (gt, STATUS_PROTOCOL, gpgme_get_protocol_name (proto),
NULL);
return 0;
}
gpg_error_t
gt_set_armor (gpgme_tool_t gt, int armor)
{
gpgme_set_armor (gt->ctx, armor);
return 0;
}
gpg_error_t
gt_get_armor (gpgme_tool_t gt)
{
gt_write_status (gt, STATUS_ARMOR,
gpgme_get_armor (gt->ctx) ? "true" : "false", NULL);
return 0;
}
gpg_error_t
gt_set_textmode (gpgme_tool_t gt, int textmode)
{
gpgme_set_textmode (gt->ctx, textmode);
return 0;
}
gpg_error_t
gt_get_textmode (gpgme_tool_t gt)
{
gt_write_status (gt, STATUS_TEXTMODE,
gpgme_get_textmode (gt->ctx) ? "true" : "false", NULL);
return 0;
}
gpg_error_t
gt_set_keylist_mode (gpgme_tool_t gt, gpgme_keylist_mode_t keylist_mode)
{
gpgme_set_keylist_mode (gt->ctx, keylist_mode);
return 0;
}
gpg_error_t
gt_get_keylist_mode (gpgme_tool_t gt)
{
#define NR_KEYLIST_MODES 6
const char *modes[NR_KEYLIST_MODES + 1];
int idx = 0;
gpgme_keylist_mode_t mode = gpgme_get_keylist_mode (gt->ctx);
if (mode & GPGME_KEYLIST_MODE_LOCAL)
modes[idx++] = "local";
if (mode & GPGME_KEYLIST_MODE_EXTERN)
modes[idx++] = "extern";
if (mode & GPGME_KEYLIST_MODE_SIGS)
modes[idx++] = "sigs";
if (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)
modes[idx++] = "sig_notations";
if (mode & GPGME_KEYLIST_MODE_EPHEMERAL)
modes[idx++] = "ephemeral";
if (mode & GPGME_KEYLIST_MODE_VALIDATE)
modes[idx++] = "validate";
modes[idx++] = NULL;
gt_write_status (gt, STATUS_KEYLIST_MODE, modes[0], modes[1], modes[2],
modes[3], modes[4], modes[5], modes[6]);
return 0;
}
gpg_error_t
gt_set_include_certs (gpgme_tool_t gt, int include_certs)
{
gpgme_set_include_certs (gt->ctx, include_certs);
return 0;
}
gpg_error_t
gt_get_include_certs (gpgme_tool_t gt)
{
int include_certs = gpgme_get_include_certs (gt->ctx);
char buf[100];
if (include_certs == GPGME_INCLUDE_CERTS_DEFAULT)
strcpy (buf, "default");
else
snprintf (buf, sizeof (buf), "%i", include_certs);
gt_write_status (gt, STATUS_INCLUDE_CERTS, buf, NULL);
return 0;
}
gpg_error_t
gt_decrypt_verify (gpgme_tool_t gt, gpgme_data_t cipher, gpgme_data_t plain,
int verify)
{
if (verify)
return gpgme_op_decrypt_verify (gt->ctx, cipher, plain);
else
return gpgme_op_decrypt (gt->ctx, cipher, plain);
}
gpg_error_t
gt_sign_encrypt (gpgme_tool_t gt, gpgme_encrypt_flags_t flags,
gpgme_data_t plain, gpgme_data_t cipher, int sign)
{
gpg_error_t err;
if (sign)
err = gpgme_op_encrypt (gt->ctx, gt->recipients, flags, plain, cipher);
else
err = gpgme_op_encrypt_sign (gt->ctx, gt->recipients, flags, plain, cipher);
gt_recipients_clear (gt);
return err;
}
gpg_error_t
gt_sign (gpgme_tool_t gt, gpgme_data_t plain, gpgme_data_t sig,
gpgme_sig_mode_t mode)
{
return gpgme_op_sign (gt->ctx, plain, sig, mode);
}
gpg_error_t
gt_verify (gpgme_tool_t gt, gpgme_data_t sig, gpgme_data_t sig_text,
gpgme_data_t plain)
{
return gpgme_op_verify (gt->ctx, sig, sig_text, plain);
}
gpg_error_t
gt_import (gpgme_tool_t gt, gpgme_data_t data)
{
return gpgme_op_import (gt->ctx, data);
}
gpg_error_t
gt_export (gpgme_tool_t gt, const char *pattern[], gpgme_export_mode_t mode,
gpgme_data_t data)
{
return gpgme_op_export_ext (gt->ctx, pattern, mode, data);
}
gpg_error_t
gt_genkey (gpgme_tool_t gt, const char *parms, gpgme_data_t public,
gpgme_data_t secret)
{
return gpgme_op_genkey (gt->ctx, parms, public, secret);
}
gpg_error_t
gt_import_keys (gpgme_tool_t gt, char *fpr[])
{
gpg_error_t err;
int cnt;
int idx;
gpgme_key_t *keys;
cnt = 0;
while (fpr[cnt])
cnt++;
if (! cnt)
return gpg_error (GPG_ERR_INV_VALUE);
keys = malloc ((cnt + 1) * sizeof (gpgme_key_t));
if (! keys)
return gpg_error_from_syserror ();
for (idx = 0; idx < cnt; idx++)
{
err = gpgme_get_key (gt->ctx, fpr[idx], &keys[idx], 0);
if (err)
break;
}
if (! err)
{
keys[cnt] = NULL;
err = gpgme_op_import_keys (gt->ctx, keys);
}
/* Rollback. */
while (--idx >= 0)
gpgme_key_unref (keys[idx]);
free (keys);
return err;
}
gpg_error_t
gt_delete (gpgme_tool_t gt, char *fpr, int allow_secret)
{
gpg_error_t err;
gpgme_key_t key;
err = gpgme_get_key (gt->ctx, fpr, &key, 0);
if (err)
return err;
err = gpgme_op_delete (gt->ctx, key, allow_secret);
gpgme_key_unref (key);
return err;
}
gpg_error_t
gt_keylist_start (gpgme_tool_t gt, const char *pattern[], int secret_only)
{
return gpgme_op_keylist_ext_start (gt->ctx, pattern, secret_only, 0);
}
gpg_error_t
gt_keylist_next (gpgme_tool_t gt, gpgme_key_t *key)
{
return gpgme_op_keylist_next (gt->ctx, key);
}
gpg_error_t
gt_getauditlog (gpgme_tool_t gt, gpgme_data_t output, unsigned int flags)
{
return gpgme_op_getauditlog (gt->ctx, output, flags);
}
gpg_error_t
gt_vfs_mount (gpgme_tool_t gt, const char *container_file,
const char *mount_dir, int flags)
{
gpg_error_t err;
gpg_error_t op_err;
err = gpgme_op_vfs_mount (gt->ctx, container_file, mount_dir, flags, &op_err);
return err || op_err;
}
// TODO
#define GT_RESULT_ENCRYPT 0x1
#define GT_RESULT_DECRYPT 0x2
#define GT_RESULT_SIGN 0x4
#define GT_RESULT_VERIFY 0x8
#define GT_RESULT_IMPORT 0x10
#define GT_RESULT_GENKEY 0x20
#define GT_RESULT_KEYLIST 0x40
#define GT_RESULT_VFS_MOUNT 0x80
#define GT_RESULT_ALL (~0U)
gpg_error_t
gt_result (gpgme_tool_t gt, unsigned int flags)
{
if (flags & GT_RESULT_ENCRYPT)
{
gpgme_encrypt_result_t res = gpgme_op_encrypt_result (gt->ctx);
if (res)
{
gpgme_invalid_key_t invrec = res->invalid_recipients;
while (invrec)
{
gt_write_status (gt, STATUS_ENCRYPT_RESULT, "invalid_recipient",
invrec->fpr, invrec->reason);
invrec = invrec->next;
}
}
}
return 0;
}
/* GPGME SERVER. */
#include <assuan.h>
struct server
{
gpgme_tool_t gt;
assuan_context_t assuan_ctx;
gpgme_data_encoding_t input_enc;
gpgme_data_encoding_t output_enc;
assuan_fd_t message_fd;
gpgme_data_encoding_t message_enc;
};
gpg_error_t
server_write_status (void *hook, const char *status, const char *msg)
{
struct server *server = hook;
return assuan_write_status (server->assuan_ctx, status, msg);
}
static gpgme_data_encoding_t
server_data_encoding (const char *line)
{
if (strstr (line, "--binary"))
return GPGME_DATA_ENCODING_BINARY;
if (strstr (line, "--base64"))
return GPGME_DATA_ENCODING_BASE64;
if (strstr (line, "--armor"))
return GPGME_DATA_ENCODING_ARMOR;
if (strstr (line, "--url"))
return GPGME_DATA_ENCODING_URL;
if (strstr (line, "--urlesc"))
return GPGME_DATA_ENCODING_URLESC;
if (strstr (line, "--url0"))
return GPGME_DATA_ENCODING_URL0;
return GPGME_DATA_ENCODING_NONE;
}
static gpgme_error_t
server_data_obj (assuan_fd_t fd, gpgme_data_encoding_t encoding,
gpgme_data_t *data)
{
gpgme_error_t err;
err = gpgme_data_new_from_fd (data, fd);
if (err)
return err;
return gpgme_data_set_encoding (*data, encoding);
}
void
server_reset_fds (struct server *server)
{
/* assuan closes the input and output FDs for us when doing a RESET,
but we use this same function after commands, so repeat it
here. */
assuan_close_input_fd (server->assuan_ctx);
assuan_close_output_fd (server->assuan_ctx);
if (server->message_fd != -1)
{
/* FIXME: Assuan should provide a close function. */
close (server->message_fd);
server->message_fd = -1;
}
server->input_enc = GPGME_DATA_ENCODING_NONE;
server->output_enc = GPGME_DATA_ENCODING_NONE;
server->message_enc = GPGME_DATA_ENCODING_NONE;
}
static gpg_error_t
reset_notify (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
server_reset_fds (server);
gt_reset (server->gt);
return 0;
}
static gpg_error_t
cmd_version (assuan_context_t ctx, char *line)
{
if (line && *line)
{
const char *version = gpgme_check_version (line);
return version ? 0 : gpg_error (GPG_ERR_SELFTEST_FAILED);
}
else
{
const char *version = gpgme_check_version (NULL);
return assuan_send_data (ctx, version, strlen (version));
}
}
static gpg_error_t
cmd_engine (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
return gt_get_engine_info (server->gt, gt_protocol_from_name (line));
}
static gpg_error_t
cmd_protocol (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
return gt_set_protocol (server->gt, gt_protocol_from_name (line));
else
return gt_get_protocol (server->gt);
}
static gpg_error_t
cmd_armor (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
{
int flag = 0;
if (! strcasecmp (line, "true") || ! strcasecmp (line, "yes")
|| line[0] == '1')
flag = 1;
return gt_set_armor (server->gt, flag);
}
else
return gt_get_armor (server->gt);
}
static gpg_error_t
cmd_textmode (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
{
int flag = 0;
if (! strcasecmp (line, "true") || ! strcasecmp (line, "yes")
|| line[0] == '1')
flag = 1;
return gt_set_textmode (server->gt, flag);
}
else
return gt_get_textmode (server->gt);
}
static gpg_error_t
cmd_include_certs (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
{
int include_certs = 0;
if (! strcasecmp (line, "default"))
include_certs = GPGME_INCLUDE_CERTS_DEFAULT;
else
include_certs = atoi (line);
return gt_set_include_certs (server->gt, include_certs);
}
else
return gt_get_include_certs (server->gt);
}
static gpg_error_t
cmd_keylist_mode (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
{
gpgme_keylist_mode_t mode = 0;
if (strstr (line, "local"))
mode |= GPGME_KEYLIST_MODE_LOCAL;
if (strstr (line, "extern"))
mode |= GPGME_KEYLIST_MODE_EXTERN;
if (strstr (line, "sigs"))
mode |= GPGME_KEYLIST_MODE_SIGS;
if (strstr (line, "sig_notations"))
mode |= GPGME_KEYLIST_MODE_SIG_NOTATIONS;
if (strstr (line, "ephemeral"))
mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
if (strstr (line, "validate"))
mode |= GPGME_KEYLIST_MODE_VALIDATE;
return gt_set_keylist_mode (server->gt, mode);
}
else
return gt_get_keylist_mode (server->gt);
}
static void
input_notify (assuan_context_t ctx, const char *line)
{
struct server *server = assuan_get_pointer (ctx);
server->input_enc = server_data_encoding (line);
}
static void
output_notify (assuan_context_t ctx, const char *line)
{
struct server *server = assuan_get_pointer (ctx);
server->output_enc = server_data_encoding (line);
}
static gpg_error_t
cmd_message (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t sysfd;
err = assuan_command_parse_fd (ctx, line, &sysfd);
if (err)
return err;
server->message_fd = sysfd;
server->message_enc = server_data_encoding (line);
return 0;
}
static gpg_error_t
cmd_recipient (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
return gt_recipients_add (server->gt, line);
}
static gpg_error_t
cmd_signer (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
return gt_signers_add (server->gt, line);
}
static gpg_error_t
cmd_signers_clear (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
return gt_signers_clear (server->gt);
}
static gpg_error_t
_cmd_decrypt_verify (assuan_context_t ctx, char *line, int verify)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t inp_fd;
assuan_fd_t out_fd;
gpgme_data_t inp_data;
gpgme_data_t out_data;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
out_fd = assuan_get_output_fd (ctx);
if (out_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_OUTPUT;
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
{
gpgme_data_release (inp_data);
return err;
}
err = gt_decrypt_verify (server->gt, inp_data, out_data, verify);
gpgme_data_release (inp_data);
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_decrypt (assuan_context_t ctx, char *line)
{
return _cmd_decrypt_verify (ctx, line, 0);
}
static gpg_error_t
cmd_decrypt_verify (assuan_context_t ctx, char *line)
{
return _cmd_decrypt_verify (ctx, line, 1);
}
static gpg_error_t
_cmd_sign_encrypt (assuan_context_t ctx, char *line, int sign)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t inp_fd;
assuan_fd_t out_fd;
gpgme_data_t inp_data;
gpgme_data_t out_data;
gpgme_encrypt_flags_t flags = 0;
if (strstr (line, "--always-trust"))
flags |= GPGME_ENCRYPT_ALWAYS_TRUST;
if (strstr (line, "--no-encrypt-to"))
flags |= GPGME_ENCRYPT_NO_ENCRYPT_TO;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
out_fd = assuan_get_output_fd (ctx);
if (out_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_OUTPUT;
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
{
gpgme_data_release (inp_data);
return err;
}
err = gt_sign_encrypt (server->gt, flags, inp_data, out_data, sign);
gpgme_data_release (inp_data);
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_encrypt (assuan_context_t ctx, char *line)
{
return _cmd_sign_encrypt (ctx, line, 0);
}
static gpg_error_t
cmd_sign_encrypt (assuan_context_t ctx, char *line)
{
return _cmd_sign_encrypt (ctx, line, 1);
}
static gpg_error_t
cmd_sign (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t inp_fd;
assuan_fd_t out_fd;
gpgme_data_t inp_data;
gpgme_data_t out_data;
gpgme_sig_mode_t mode = GPGME_SIG_MODE_NORMAL;
if (strstr (line, "--clear"))
mode = GPGME_SIG_MODE_CLEAR;
if (strstr (line, "--detach"))
mode = GPGME_SIG_MODE_DETACH;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
out_fd = assuan_get_output_fd (ctx);
if (out_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_OUTPUT;
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
{
gpgme_data_release (inp_data);
return err;
}
err = gt_sign (server->gt, inp_data, out_data, mode);
gpgme_data_release (inp_data);
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_verify (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t inp_fd;
assuan_fd_t msg_fd;
assuan_fd_t out_fd;
gpgme_data_t inp_data;
gpgme_data_t msg_data = NULL;
gpgme_data_t out_data = NULL;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
msg_fd = server->message_fd;
out_fd = assuan_get_output_fd (ctx);
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
if (msg_fd != ASSUAN_INVALID_FD)
{
err = server_data_obj (msg_fd, server->message_enc, &msg_data);
if (err)
{
gpgme_data_release (inp_data);
return err;
}
}
if (out_fd != ASSUAN_INVALID_FD)
{
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
{
gpgme_data_release (inp_data);
gpgme_data_release (msg_data);
return err;
}
}
err = gt_verify (server->gt, inp_data, msg_data, out_data);
gpgme_data_release (inp_data);
if (msg_data)
gpgme_data_release (msg_data);
if (out_data)
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_import (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
if (line && *line)
{
char *fprs[2] = { line, NULL };
return gt_import_keys (server->gt, fprs);
}
else
{
gpg_error_t err;
assuan_fd_t inp_fd;
gpgme_data_t inp_data;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
err = gt_import (server->gt, inp_data);
gpgme_data_release (inp_data);
server_reset_fds (server);
return err;
}
}
static gpg_error_t
cmd_export (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t out_fd;
gpgme_data_t out_data;
gpgme_export_mode_t mode = 0;
const char *pattern[2];
const char optstr[] = "--extern ";
out_fd = assuan_get_output_fd (ctx);
if (out_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_OUTPUT;
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
return err;
if (strncasecmp (line, optstr, strlen (optstr)))
{
mode |= GPGME_EXPORT_MODE_EXTERN;
line += strlen (optstr);
}
pattern[0] = line;
pattern[1] = NULL;
err = gt_export (server->gt, pattern, mode, out_data);
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
_cmd_genkey_write (gpgme_data_t data, const void *buf, size_t size)
{
while (size > 0)
{
ssize_t writen = gpgme_data_write (data, buf, size);
if (writen < 0 && errno != EAGAIN)
return gpg_error_from_syserror ();
else if (writen > 0)
{
buf = (void *) (((char *) buf) + writen);
size -= writen;
}
}
return 0;
}
static gpg_error_t
cmd_genkey (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t inp_fd;
assuan_fd_t out_fd;
gpgme_data_t inp_data;
gpgme_data_t out_data = NULL;
gpgme_data_t parms_data = NULL;
const char *parms;
inp_fd = assuan_get_input_fd (ctx);
if (inp_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_INPUT;
out_fd = assuan_get_output_fd (ctx);
err = server_data_obj (inp_fd, server->input_enc, &inp_data);
if (err)
return err;
if (out_fd != ASSUAN_INVALID_FD)
{
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
{
gpgme_data_release (inp_data);
return err;
}
}
/* Convert input data. */
err = gpgme_data_new (&parms_data);
if (err)
goto out;
do
{
char buf[512];
ssize_t readn = gpgme_data_read (inp_data, buf, sizeof (buf));
if (readn < 0)
{
err = gpg_error_from_syserror ();
goto out;
}
else if (readn == 0)
break;
err = _cmd_genkey_write (parms_data, buf, readn);
if (err)
goto out;
}
while (1);
err = _cmd_genkey_write (parms_data, "", 1);
if (err)
goto out;
parms = gpgme_data_release_and_get_mem (parms_data, NULL);
parms_data = NULL;
if (! parms)
{
err = gpg_error (GPG_ERR_GENERAL);
goto out;
}
err = gt_genkey (server->gt, parms, out_data, NULL);
server_reset_fds (server);
out:
gpgme_data_release (inp_data);
if (out_data)
gpgme_data_release (out_data);
if (parms_data)
gpgme_data_release (parms_data);
return err;
}
static gpg_error_t
cmd_delete (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
int allow_secret = 0;
const char optstr[] = "--allow-secret ";
if (strncasecmp (line, optstr, strlen (optstr)))
{
allow_secret = 1;
line += strlen (optstr);
}
return gt_delete (server->gt, line, allow_secret);
}
static gpg_error_t
cmd_keylist (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
int secret_only = 0;
const char *pattern[2];
const char optstr[] = "--secret-only ";
if (strncasecmp (line, optstr, strlen (optstr)))
{
secret_only = 1;
line += strlen (optstr);
}
pattern[0] = line;
pattern[1] = NULL;
err = gt_keylist_start (server->gt, pattern, secret_only);
while (! err)
{
gpgme_key_t key;
err = gt_keylist_next (server->gt, &key);
if (gpg_err_code (err) == GPG_ERR_EOF)
{
err = 0;
break;
}
else if (! err)
{
char buf[100];
/* FIXME: More data. */
snprintf (buf, sizeof (buf), "key:%s\n", key->subkeys->fpr);
assuan_send_data (ctx, buf, strlen (buf));
gpgme_key_unref (key);
}
}
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_getauditlog (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
gpg_error_t err;
assuan_fd_t out_fd;
gpgme_data_t out_data;
out_fd = assuan_get_output_fd (ctx);
if (out_fd == ASSUAN_INVALID_FD)
return GPG_ERR_ASS_NO_OUTPUT;
err = server_data_obj (out_fd, server->output_enc, &out_data);
if (err)
return err;
err = gt_getauditlog (server->gt, out_data, 0);
gpgme_data_release (out_data);
server_reset_fds (server);
return err;
}
static gpg_error_t
cmd_vfs_mount (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
char *mount_dir;
gpg_error_t err;
mount_dir = strchr (line, ' ');
if (mount_dir)
{
*(mount_dir++) = '\0';
while (*mount_dir == ' ')
mount_dir++;
}
err = gt_vfs_mount (server->gt, line, mount_dir, 0);
return err;
}
static gpg_error_t
cmd_result (assuan_context_t ctx, char *line)
{
struct server *server = assuan_get_pointer (ctx);
return gt_result (server->gt, GT_RESULT_ALL);
}
/* STRERROR <err> */
static gpg_error_t
cmd_strerror (assuan_context_t ctx, char *line)
{
gpg_error_t err;
char buf[100];
err = atoi (line);
snprintf (buf, sizeof (buf), "%s <%s>", gpgme_strerror (err),
gpgme_strsource (err));
return assuan_send_data (ctx, buf, strlen (buf));
}
static gpg_error_t
cmd_pubkey_algo_name (assuan_context_t ctx, char *line)
{
gpgme_pubkey_algo_t algo;
char buf[100];
algo = atoi (line);
snprintf (buf, sizeof (buf), "%s", gpgme_pubkey_algo_name (algo));
return assuan_send_data (ctx, buf, strlen (buf));
}
static gpg_error_t
cmd_hash_algo_name (assuan_context_t ctx, char *line)
{
gpgme_hash_algo_t algo;
char buf[100];
algo = atoi (line);
snprintf (buf, sizeof (buf), "%s", gpgme_hash_algo_name (algo));
return assuan_send_data (ctx, buf, strlen (buf));
}
/* Tell the assuan library about our commands. */
static gpg_error_t
register_commands (assuan_context_t ctx)
{
gpg_error_t err;
static struct {
const char *name;
gpg_error_t (*handler)(assuan_context_t, char *line);
} table[] = {
// RESET, BYE are implicit.
{ "VERSION", cmd_version },
// TODO: Set engine info.
{ "ENGINE", cmd_engine },
{ "PROTOCOL", cmd_protocol },
{ "ARMOR", cmd_armor },
{ "TEXTMODE", cmd_textmode },
{ "INCLUDE_CERTS", cmd_include_certs },
{ "KEYLIST_MODE", cmd_keylist_mode },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "MESSAGE", cmd_message },
{ "RECIPIENT", cmd_recipient },
{ "SIGNER", cmd_signer },
{ "SIGNERS_CLEAR", cmd_signers_clear },
// TODO: SIGNOTATION missing.
// TODO: Could add wait interface if we allow more than one context
// and add _START variants.
// TODO: Could add data interfaces if we allow multiple data objects.
{ "DECRYPT", cmd_decrypt },
{ "DECRYPT_VERIFY", cmd_decrypt_verify },
{ "ENCRYPT", cmd_encrypt },
{ "ENCRYPT_SIGN", cmd_sign_encrypt },
{ "SIGN_ENCRYPT", cmd_sign_encrypt },
{ "SIGN", cmd_sign },
{ "VERIFY", cmd_verify },
{ "IMPORT", cmd_import },
{ "EXPORT", cmd_export },
{ "GENKEY", cmd_genkey },
{ "DELETE", cmd_delete },
// TODO: EDIT, CARD_EDIT (with INQUIRE)
{ "KEYLIST", cmd_keylist },
{ "LISTKEYS", cmd_keylist },
// TODO: TRUSTLIST, TRUSTLIST_EXT
{ "GETAUDITLOG", cmd_getauditlog },
// TODO: ASSUAN
{ "VFS_MOUNT", cmd_vfs_mount },
{ "MOUNT", cmd_vfs_mount },
// TODO: GPGCONF
{ "RESULT", cmd_result },
{ "STRERROR", cmd_strerror },
{ "PUBKEY_ALGO_NAME", cmd_pubkey_algo_name },
{ "HASH_ALGO_NAME", cmd_hash_algo_name },
{ NULL }
};
int idx;
for (idx = 0; table[idx].name; idx++)
{
err = assuan_register_command (ctx, table[idx].name, table[idx].handler);
if (err)
return err;
}
return 0;
}
/* TODO: password callback can do INQUIRE. */
void
gpgme_server (gpgme_tool_t gt)
{
gpg_error_t err;
int filedes[2];
struct server server;
static const char hello[] = ("GPGME-Tool " VERSION " ready");
memset (&server, 0, sizeof (server));
server.message_fd = -1;
server.input_enc = GPGME_DATA_ENCODING_NONE;
server.output_enc = GPGME_DATA_ENCODING_NONE;
server.message_enc = GPGME_DATA_ENCODING_NONE;
server.gt = gt;
gt->write_status = server_write_status;
gt->write_status_hook = &server;
/* We use a pipe based server so that we can work from scripts.
assuan_init_pipe_server will automagically detect when we are
called with a socketpair and ignore FIELDES in this case. */
filedes[0] = 0;
filedes[1] = 1;
err = assuan_new (&server.assuan_ctx);
if (err)
log_error (1, err, "can't create assuan context");
assuan_set_pointer (server.assuan_ctx, &server);
err = assuan_init_pipe_server (server.assuan_ctx, filedes);
if (err)
log_error (1, err, "can't initialize assuan server");
err = register_commands (server.assuan_ctx);
if (err)
log_error (1, err, "can't register assuan commands");
assuan_set_hello_line (server.assuan_ctx, hello);
assuan_register_reset_notify (server.assuan_ctx, reset_notify);
assuan_register_input_notify (server.assuan_ctx, input_notify);
assuan_register_output_notify (server.assuan_ctx, output_notify);
#define DBG_ASSUAN 0
if (DBG_ASSUAN)
assuan_set_log_stream (server.assuan_ctx, log_stream);
for (;;)
{
err = assuan_accept (server.assuan_ctx);
if (err == -1)
break;
else if (err)
{
log_error (0, err, "assuan accept problem");
break;
}
err = assuan_process (server.assuan_ctx);
if (err)
log_error (0, err, "assuan processing failed");
}
assuan_release (server.assuan_ctx);
}
/* 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 -- invoke GPGME operations";
static char args_doc[] = "COMMAND [OPTIONS...]";
static struct argp_option options[] = {
{ "server", 's', 0, 0, "Server mode" },
{ 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;
};
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)
{
struct args *args = state->input;
switch (key)
{
case 's':
args->cmd = CMD_SERVER;
break;
#if 0
case ARGP_KEY_ARG:
if (state->arg_num >= 2)
argp_usage (state);
printf ("Arg[%i] = %s\n", state->arg_num, arg);
break;
case ARGP_KEY_END:
if (state->arg_num < 2)
argp_usage (state);
break;
#endif
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
int
main (int argc, char *argv[])
{
struct args args;
struct gpgme_tool gt;
setlocale (LC_ALL, "");
gpgme_check_version (NULL);
gpgme_set_locale (NULL, LC_CTYPE, setlocale (LC_CTYPE, NULL));
#ifdef LC_MESSAGES
gpgme_set_locale (NULL, LC_MESSAGES, setlocale (LC_MESSAGES, NULL));
#endif
args_init (&args);
argp_parse (&argp, argc, argv, 0, 0, &args);
log_init ();
gt_init (&gt);
switch (args.cmd)
{
case CMD_DEFAULT:
case CMD_SERVER:
gpgme_server (&gt);
break;
}
gpgme_release (gt.ctx);
return 0;
}