summaryrefslogtreecommitdiff
path: root/src/gpgme-tool.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gpgme-tool.c')
-rw-r--r--src/gpgme-tool.c660
1 files changed, 544 insertions, 116 deletions
diff --git a/src/gpgme-tool.c b/src/gpgme-tool.c
index 9591d15..978c387 100644
--- a/src/gpgme-tool.c
+++ b/src/gpgme-tool.c
@@ -1,18 +1,19 @@
/* gpgme-tool.c - Assuan server exposing GnuPG Made Easy operations.
- Copyright (C) 2009, 2010 g10 Code GmbH
+ Copyright (C) 2009, 2010, 2012, 2013 g10 Code GmbH
+ Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
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.
-
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
*/
@@ -40,10 +41,10 @@
#include "gpgme.h"
/* GCC attributes. */
-#if __GNUC__ >= 4
+#if __GNUC__ >= 4
# define GT_GCC_A_SENTINEL(a) __attribute__ ((sentinel(a)))
#else
-# define GT_GCC_A_SENTINEL(a)
+# define GT_GCC_A_SENTINEL(a)
#endif
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
@@ -158,9 +159,9 @@ struct argp
| ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR)
-void argp_error (const struct argp_state *state,
+void argp_error (const struct argp_state *state,
const char *fmt, ...) GT_GCC_A_PRINTF(2, 3);
-
+
char *
@@ -374,10 +375,10 @@ argp_parse (const struct argp *argp, int argc,
*arg = '\0';
arg++;
}
-
+
if (state.argv[idx][1] != '-')
key = state.argv[idx][1];
-
+
while (! found && opt->key)
{
if (key == opt->key
@@ -453,7 +454,7 @@ argp_parse (const struct argp *argp, int argc,
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);
@@ -474,6 +475,148 @@ argp_parse (const struct argp *argp, int argc,
#endif
+/* MEMBUF */
+
+/* A simple implementation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out-of-core checks. */
+
+/* The definition of the structure is private, we only need it here,
+ so it can be allocated on the stack. */
+struct private_membuf_s
+{
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+typedef struct private_membuf_s membuf_t;
+
+/* Return the current length of the membuf. */
+#define get_membuf_len(a) ((a)->len)
+#define is_membuf_ready(a) ((a)->buf || (a)->out_of_core)
+#define MEMBUF_ZERO { 0, 0, NULL, 0}
+
+
+static void
+init_membuf (membuf_t *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = malloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = errno;
+}
+
+
+/* Shift the the content of the membuf MB by AMOUNT bytes. The next
+ operation will then behave as if AMOUNT bytes had not been put into
+ the buffer. If AMOUNT is greater than the actual accumulated
+ bytes, the membuf is basically reset to its initial state. */
+#if 0 /* Not yet used. */
+static void
+clear_membuf (membuf_t *mb, size_t amount)
+{
+ /* No need to clear if we are already out of core. */
+ if (mb->out_of_core)
+ return;
+ if (amount >= mb->len)
+ mb->len = 0;
+ else
+ {
+ mb->len -= amount;
+ memmove (mb->buf, mb->buf+amount, mb->len);
+ }
+}
+#endif /* unused */
+
+static void
+put_membuf (membuf_t *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core || !len)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = realloc (mb->buf, mb->size);
+ if (!p)
+ {
+ mb->out_of_core = errno ? errno : ENOMEM;
+ return;
+ }
+ mb->buf = p;
+ }
+ memcpy (mb->buf + mb->len, buf, len);
+ mb->len += len;
+}
+
+
+#if 0 /* Not yet used. */
+static void
+put_membuf_str (membuf_t *mb, const char *string)
+{
+ put_membuf (mb, string, strlen (string));
+}
+#endif /* unused */
+
+
+static void *
+get_membuf (membuf_t *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ if (mb->buf)
+ {
+ free (mb->buf);
+ mb->buf = NULL;
+ }
+ gpg_err_set_errno (mb->out_of_core);
+ return NULL;
+ }
+
+ p = mb->buf;
+ if (len)
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
+ return p;
+}
+
+
+/* Peek at the membuf MB. On success a pointer to the buffer is
+ returned which is valid until the next operation on MB. If LEN is
+ not NULL the current LEN of the buffer is stored there. On error
+ NULL is returned and ERRNO is set. */
+#if 0 /* Not yet used. */
+static const void *
+peek_membuf (membuf_t *mb, size_t *len)
+{
+ const char *p;
+
+ if (mb->out_of_core)
+ {
+ gpg_err_set_errno (mb->out_of_core);
+ return NULL;
+ }
+
+ p = mb->buf;
+ if (len)
+ *len = mb->len;
+ return p;
+}
+#endif /* unused */
+
+
+
/* SUPPORT. */
FILE *log_stream;
char *program_name = "gpgme-tool";
@@ -481,7 +624,7 @@ char *program_name = "gpgme-tool";
#define spacep(p) (*(p) == ' ' || *(p) == '\t')
-void log_error (int status, gpg_error_t errnum,
+void log_error (int status, gpg_error_t errnum,
const char *fmt, ...) GT_GCC_A_PRINTF(3,4);
@@ -502,8 +645,11 @@ log_error (int status, gpg_error_t errnum, const char *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, ": %s", gpg_strerror (errnum));
+ if (gpg_err_source (errnum) != GPG_ERR_SOURCE_GPGME)
+ fprintf (log_stream, " <%s>", gpg_strsource (errnum));
+ }
fprintf (log_stream, "\n");
if (status)
exit (status);
@@ -518,7 +664,7 @@ strcpy_escaped_plus (char *d, const char *s)
while (*s)
{
if (*s == '%' && s[1] && s[2])
- {
+ {
s++;
*d++ = xtoi_2 (s);
s += 2;
@@ -528,7 +674,7 @@ strcpy_escaped_plus (char *d, const char *s)
else
*d++ = *s++;
}
- *d = 0;
+ *d = 0;
}
@@ -565,6 +711,12 @@ skip_options (char *line)
typedef gpg_error_t (*result_xml_write_cb_t) (void *hook, const void *buf,
size_t len);
+static char xml_preamble1[] = "<?xml version=\"1.0\" "
+ "encoding=\"UTF-8\" standalone=\"yes\"?>\n";
+static const char xml_preamble2[] = "<gpgme>\n";
+static const char xml_end[] = "</gpgme>\n";
+
+
struct result_xml_state
{
int indent;
@@ -630,7 +782,7 @@ result_xml_tag_start (struct result_xml_state *state, char *name, ...)
state->had_data[state->next_tag] = 0;
state->indent += 2;
state->next_tag++;
-
+
while (1)
{
attr = va_arg (ap, char *);
@@ -651,12 +803,56 @@ result_xml_tag_start (struct result_xml_state *state, char *name, ...)
return 0;
}
+/* Return a constant string with an XML entity for C. */
+static const char *
+result_xml_escape_replacement(char c)
+{
+ switch (c)
+ {
+ case '<':
+ return "&lt;";
+ case '>':
+ return "&gt;";
+ case '&':
+ return "&amp;";
+ default:
+ return NULL;
+ }
+}
+
+/* Escape DATA by replacing certain characters with their XML
+ entities. The result is stored in a newly allocated buffer which
+ address will be stored at BUF. Returns 0 on success. */
+static gpg_error_t
+result_xml_escape (const char *data, char **buf)
+{
+ int data_len, i;
+ const char *r;
+ membuf_t mb;
+
+ init_membuf (&mb, 128);
+ 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);
+ return *buf? 0 : gpg_error_from_syserror ();
+}
+
gpg_error_t
-result_xml_tag_data (struct result_xml_state *state, char *data)
+result_xml_tag_data (struct result_xml_state *state, const char *data)
{
+ gpg_error_t err;
result_xml_write_cb_t cb = state->cb;
void *hook = state->hook;
+ char *buf = NULL;
if (state->had_data[state->next_tag - 1])
{
@@ -668,7 +864,13 @@ result_xml_tag_data (struct result_xml_state *state, char *data)
(*cb) (hook, ">", 1);
state->had_data[state->next_tag - 1] = 2;
- (*cb) (hook, data, strlen (data));
+ err = result_xml_escape (data, &buf);
+ if (err)
+ return err;
+
+ (*cb) (hook, buf, strlen (buf));
+
+ free (buf);
return 0;
}
@@ -704,11 +906,11 @@ result_xml_tag_end (struct result_xml_state *state)
gpg_error_t
result_add_error (struct result_xml_state *state, char *name, gpg_error_t err)
-{
+{
char code[20];
char msg[1024];
snprintf (code, sizeof (code) - 1, "0x%x", err);
- snprintf (msg, sizeof (msg) - 1, "%s &lt;%s&gt;",
+ snprintf (msg, sizeof (msg) - 1, "%s <%s>",
gpg_strerror (err), gpg_strsource (err));
result_xml_tag_start (state, name, "value", code, NULL);
result_xml_tag_data (state, msg);
@@ -720,7 +922,7 @@ result_add_error (struct result_xml_state *state, char *name, gpg_error_t err)
gpg_error_t
result_add_pubkey_algo (struct result_xml_state *state,
char *name, gpgme_pubkey_algo_t algo)
-{
+{
char code[20];
char msg[80];
snprintf (code, sizeof (code) - 1, "0x%x", algo);
@@ -736,7 +938,7 @@ result_add_pubkey_algo (struct result_xml_state *state,
gpg_error_t
result_add_hash_algo (struct result_xml_state *state,
char *name, gpgme_hash_algo_t algo)
-{
+{
char code[20];
char msg[80];
snprintf (code, sizeof (code) - 1, "0x%x", algo);
@@ -751,7 +953,7 @@ result_add_hash_algo (struct result_xml_state *state,
gpg_error_t
result_add_keyid (struct result_xml_state *state, char *name, char *keyid)
-{
+{
result_xml_tag_start (state, name, NULL);
result_xml_tag_data (state, keyid);
result_xml_tag_end (state);
@@ -761,7 +963,7 @@ result_add_keyid (struct result_xml_state *state, char *name, char *keyid)
gpg_error_t
result_add_fpr (struct result_xml_state *state, char *name, char *fpr)
-{
+{
result_xml_tag_start (state, name, NULL);
result_xml_tag_data (state, fpr);
result_xml_tag_end (state);
@@ -785,7 +987,7 @@ result_add_timestamp (struct result_xml_state *state, char *name,
gpg_error_t
result_add_sig_mode (struct result_xml_state *state, char *name,
gpgme_sig_mode_t sig_mode)
-{
+{
char *mode;
char code[20];
@@ -813,9 +1015,63 @@ result_add_sig_mode (struct result_xml_state *state, char *name,
gpg_error_t
+result_add_protocol (struct result_xml_state *state, char *name,
+ gpgme_protocol_t protocol)
+{
+ const char *str;
+ char code[20];
+
+ snprintf (code, sizeof (code) - 1, "%i", protocol);
+ str = gpgme_get_protocol_name(protocol);
+ if (!str)
+ str = "invalid";
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_data (state, str);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
+result_add_validity (struct result_xml_state *state, char *name,
+ gpgme_validity_t validity)
+{
+ const char *str;
+ char code[20];
+
+ snprintf (code, sizeof (code) - 1, "%i", validity);
+ switch (validity)
+ {
+ case GPGME_VALIDITY_UNDEFINED:
+ str ="undefined";
+ break;
+ case GPGME_VALIDITY_NEVER:
+ str ="never";
+ break;
+ case GPGME_VALIDITY_MARGINAL:
+ str ="marginal";
+ break;
+ case GPGME_VALIDITY_FULL:
+ str ="full";
+ break;
+ case GPGME_VALIDITY_ULTIMATE:
+ str ="ultimate";
+ break;
+ default:
+ str ="unknown";
+ }
+
+ result_xml_tag_start (state, name, "value", code, NULL);
+ result_xml_tag_data (state, str);
+ result_xml_tag_end (state);
+ return 0;
+}
+
+
+gpg_error_t
result_add_value (struct result_xml_state *state,
char *name, unsigned int val)
-{
+{
char code[20];
snprintf (code, sizeof (code) - 1, "0x%x", val);
@@ -828,7 +1084,9 @@ result_add_value (struct result_xml_state *state,
gpg_error_t
result_add_string (struct result_xml_state *state,
char *name, char *str)
-{
+{
+ if (!str)
+ str = "";
result_xml_tag_start (state, name, NULL);
result_xml_tag_data (state, str);
result_xml_tag_end (state);
@@ -854,7 +1112,7 @@ result_encrypt_to_xml (gpgme_ctx_t ctx, int indent,
if (inv_recp)
{
result_xml_tag_start (&state, "invalid-recipients", NULL);
-
+
while (inv_recp)
{
result_xml_tag_start (&state, "invalid-key", NULL);
@@ -867,7 +1125,7 @@ result_encrypt_to_xml (gpgme_ctx_t ctx, int indent,
result_xml_tag_end (&state);
}
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -920,7 +1178,7 @@ result_decrypt_to_xml (gpgme_ctx_t ctx, int indent,
result_xml_tag_end (&state);
}
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -944,7 +1202,7 @@ result_sign_to_xml (gpgme_ctx_t ctx, int indent,
if (inv_key)
{
result_xml_tag_start (&state, "invalid-signers", NULL);
-
+
while (inv_key)
{
result_xml_tag_start (&state, "invalid-key", NULL);
@@ -980,7 +1238,7 @@ result_sign_to_xml (gpgme_ctx_t ctx, int indent,
}
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1014,7 +1272,7 @@ result_verify_to_xml (gpgme_ctx_t ctx, int indent,
while (sig)
{
result_xml_tag_start (&state, "signature", NULL);
-
+
/* FIXME: Could be done better. */
result_add_value (&state, "summary", sig->summary);
if (sig->fpr)
@@ -1032,7 +1290,7 @@ result_verify_to_xml (gpgme_ctx_t ctx, int indent,
result_add_hash_algo (&state, "hash-algo", sig->hash_algo);
if (sig->pka_address)
result_add_string (&state, "pka_address", sig->pka_address);
-
+
result_xml_tag_end (&state);
sig = sig->next;
}
@@ -1040,7 +1298,7 @@ result_verify_to_xml (gpgme_ctx_t ctx, int indent,
}
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1078,7 +1336,7 @@ result_import_to_xml (gpgme_ctx_t ctx, int indent,
if (stat)
{
result_xml_tag_start (&state, "imports", NULL);
-
+
while (stat)
{
result_xml_tag_start (&state, "import-status", NULL);
@@ -1096,7 +1354,7 @@ result_import_to_xml (gpgme_ctx_t ctx, int indent,
}
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1120,7 +1378,7 @@ result_genkey_to_xml (gpgme_ctx_t ctx, int indent,
result_add_fpr (&state, "fpr", res->fpr);
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1141,7 +1399,7 @@ result_keylist_to_xml (gpgme_ctx_t ctx, int indent,
result_add_value (&state, "truncated", res->truncated);
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1162,7 +1420,7 @@ result_vfs_mount_to_xml (gpgme_ctx_t ctx, int indent,
result_add_string (&state, "mount-dir", res->mount_dir);
result_xml_tag_end (&state);
-
+
return 0;
}
@@ -1209,8 +1467,12 @@ typedef struct gpgme_tool *gpgme_tool_t;
/* Forward declaration. */
-void gt_write_status (gpgme_tool_t gt,
+void gt_write_status (gpgme_tool_t gt,
status_t status, ...) GT_GCC_A_SENTINEL(0);
+static gpg_error_t
+server_passphrase_cb (void *opaque, const char *uid_hint, const char *info,
+ int was_bad, int fd);
+
void
_gt_progress_cb (void *opaque, const char *what,
@@ -1240,9 +1502,10 @@ _gt_gpgme_new (gpgme_tool_t gt, gpgme_ctx_t *ctx)
void
gt_init (gpgme_tool_t gt)
{
- memset (gt, '\0', sizeof (*gt));
gpg_error_t err;
+ memset (gt, '\0', sizeof (*gt));
+
err = _gt_gpgme_new (gt, &gt->ctx);
if (err)
log_error (1, err, "can't create gpgme context");
@@ -1281,7 +1544,7 @@ gt_get_key (gpgme_tool_t gt, const char *pattern, gpgme_key_t *r_key)
if (!gt || !r_key || !pattern)
return gpg_error (GPG_ERR_INV_VALUE);
-
+
ctx = gt->ctx;
err = gpgme_new (&listctx);
@@ -1343,10 +1606,10 @@ gt_get_key (gpgme_tool_t gt, const char *pattern, gpgme_key_t *r_key)
}
}
gpgme_release (listctx);
-
+
if (! err)
- gt_write_status (gt, STATUS_RECIPIENT,
- ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
+ gt_write_status (gt, STATUS_RECIPIENT,
+ ((*r_key)->subkeys && (*r_key)->subkeys->fpr) ?
(*r_key)->subkeys->fpr : "invalid", NULL);
return err;
}
@@ -1359,7 +1622,7 @@ gt_recipients_add (gpgme_tool_t gt, const char *pattern)
gpgme_key_t key;
if (gt->recipients_nr >= MAX_RECIPIENTS)
- return gpg_error_from_errno (ENOMEM);
+ return gpg_error (GPG_ERR_ENOMEM);
if (gpgme_get_protocol (gt->ctx) == GPGME_PROTOCOL_UISERVER)
err = gpgme_key_from_uid (&key, pattern);
@@ -1390,7 +1653,7 @@ gt_reset (gpgme_tool_t gt)
{
gpg_error_t err;
gpgme_ctx_t ctx;
-
+
err = _gt_gpgme_new (gt, &ctx);
if (err)
return err;
@@ -1482,7 +1745,7 @@ gt_protocol_from_name (const char *name)
return GPGME_PROTOCOL_UNKNOWN;
}
-
+
gpg_error_t
gt_set_protocol (gpgme_tool_t gt, gpgme_protocol_t proto)
{
@@ -1522,6 +1785,19 @@ gt_get_sub_protocol (gpgme_tool_t gt)
gpg_error_t
+gt_set_pinentry_mode (gpgme_tool_t gt, gpgme_pinentry_mode_t mode, void *opaque)
+{
+ gpg_error_t err;
+
+ gpgme_set_passphrase_cb (gt->ctx, NULL, NULL);
+ err = gpgme_set_pinentry_mode (gt->ctx, mode);
+ if (!err && mode == GPGME_PINENTRY_MODE_LOOPBACK)
+ gpgme_set_passphrase_cb (gt->ctx, server_passphrase_cb, opaque);
+ return err;
+}
+
+
+gpg_error_t
gt_set_armor (gpgme_tool_t gt, int armor)
{
gpgme_set_armor (gt->ctx, armor);
@@ -1572,7 +1848,7 @@ gt_get_keylist_mode (gpgme_tool_t gt)
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)
@@ -1693,18 +1969,18 @@ gt_import_keys (gpgme_tool_t gt, char *fpr[])
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);
@@ -1716,7 +1992,7 @@ gt_import_keys (gpgme_tool_t gt, char *fpr[])
keys[cnt] = NULL;
err = gpgme_op_import_keys (gt->ctx, keys);
}
-
+
/* Rollback. */
while (--idx >= 0)
gpgme_key_unref (keys[idx]);
@@ -1786,7 +2062,7 @@ gt_vfs_create (gpgme_tool_t gt, const char *container_file, int flags)
}
-static const char hlp_passwd[] =
+static const char hlp_passwd[] =
"PASSWD <user-id>\n"
"\n"
"Ask the backend to change the passphrase for the key\n"
@@ -1820,10 +2096,6 @@ gt_passwd (gpgme_tool_t gt, char *fpr)
gpg_error_t
gt_result (gpgme_tool_t gt, unsigned int flags)
{
- static const char xml_preamble1[] = "<?xml version=\"1.0\" "
- "encoding=\"UTF-8\" standalone=\"yes\"?>\n";
- static const char xml_preamble2[] = "<gpgme>\n";
- static const char xml_end[] = "</gpgme>\n";
int indent = 2;
gt_write_data (gt, xml_preamble1, sizeof (xml_preamble1));
@@ -1900,7 +2172,45 @@ server_write_data (void *hook, const void *buf, size_t len)
}
+static gpg_error_t
+server_passphrase_cb (void *opaque, const char *uid_hint, const char *info,
+ int was_bad, int fd)
+{
+ struct server *server = opaque;
+ gpg_error_t err;
+ unsigned char *buf = NULL;
+ size_t buflen = 0;
+
+ if (server && server->assuan_ctx)
+ {
+ if (uid_hint)
+ assuan_write_status (server->assuan_ctx, "USERID_HINT", uid_hint);
+ if (info)
+ assuan_write_status (server->assuan_ctx, "NEED_PASSPHRASE", info);
+
+ err = assuan_inquire (server->assuan_ctx, "PASSPHRASE",
+ &buf, &buflen, 100);
+ }
+ else
+ err = gpg_error (GPG_ERR_NO_PASSPHRASE);
+
+ if (!err)
+ {
+ /* We take care to always send a LF. */
+ if (gpgme_io_writen (fd, buf, buflen))
+ err = gpg_error_from_syserror ();
+ else if (!memchr (buf, '\n', buflen) && gpgme_io_writen (fd, "\n", 1))
+ err = gpg_error_from_syserror ();
+ }
+ free (buf);
+ return err;
+}
+
+/* Wrapper around assuan_command_parse_fd to also handle a
+ "file=FILENAME" argument. On success either a filename is returned
+ at FILENAME or a file descriptor at RFD; the other one is set to
+ NULL respective ASSUAN_INVALID_FD. */
static gpg_error_t
server_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd,
char **filename)
@@ -1922,7 +2232,7 @@ server_parse_fd (assuan_context_t ctx, char *line, assuan_fd_t *rfd,
else
return assuan_command_parse_fd (ctx, line, rfd);
}
-
+
static gpgme_data_encoding_t
server_data_encoding (const char *line)
@@ -1974,8 +2284,24 @@ 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->input_fd != ASSUAN_INVALID_FD)
+ {
+#if HAVE_W32_SYSTEM
+ CloseHandle (server->input_fd);
+#else
+ close (server->input_fd);
+#endif
+ server->input_fd = ASSUAN_INVALID_FD;
+ }
+ if (server->output_fd != ASSUAN_INVALID_FD)
+ {
+#if HAVE_W32_SYSTEM
+ CloseHandle (server->output_fd);
+#else
+ close (server->output_fd);
+#endif
+ server->output_fd = ASSUAN_INVALID_FD;
+ }
if (server->message_fd != ASSUAN_INVALID_FD)
{
/* FIXME: Assuan should provide a close function. */
@@ -2033,7 +2359,7 @@ reset_notify (assuan_context_t ctx, char *line)
}
-static const char hlp_version[] =
+static const char hlp_version[] =
"VERSION [<string>]\n"
"\n"
"Call the function gpgme_check_version.";
@@ -2065,7 +2391,7 @@ cmd_engine (assuan_context_t ctx, char *line)
}
-static const char hlp_protocol[] =
+static const char hlp_protocol[] =
"PROTOCOL [<name>]\n"
"\n"
"With NAME, set the protocol. Without, return the current\n"
@@ -2097,6 +2423,39 @@ cmd_sub_protocol (assuan_context_t ctx, char *line)
}
+static const char hlp_pinentry_mode[] =
+ "PINENTRY_MODE <name>\n"
+ "\n"
+ "Set the pinentry mode to NAME. Allowedvalues for NAME are:\n"
+ " default - reset to the default of the engine,\n"
+ " ask - force the use of the pinentry,\n"
+ " cancel - emulate use of pinentry's cancel button,\n"
+ " error - return a pinentry error,\n"
+ " loopback - redirect pinentry queries to the caller.\n"
+ "Note that only recent versions of GPG support changing the pinentry mode.";
+static gpg_error_t
+cmd_pinentry_mode (assuan_context_t ctx, char *line)
+{
+ struct server *server = assuan_get_pointer (ctx);
+ gpgme_pinentry_mode_t mode;
+
+ if (!line || !*line || !strcmp (line, "default"))
+ mode = GPGME_PINENTRY_MODE_DEFAULT;
+ else if (!strcmp (line, "ask"))
+ mode = GPGME_PINENTRY_MODE_ASK;
+ else if (!strcmp (line, "cancel"))
+ mode = GPGME_PINENTRY_MODE_CANCEL;
+ else if (!strcmp (line, "error"))
+ mode = GPGME_PINENTRY_MODE_ERROR;
+ else if (!strcmp (line, "loopback"))
+ mode = GPGME_PINENTRY_MODE_LOOPBACK;
+ else
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ return gt_set_pinentry_mode (server->gt, mode, server);
+}
+
+
static const char hlp_armor[] =
"ARMOR [true|false]\n"
"\n"
@@ -2109,11 +2468,11 @@ cmd_armor (assuan_context_t ctx, char *line)
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
@@ -2137,7 +2496,7 @@ cmd_textmode (assuan_context_t ctx, char *line)
if (! strcasecmp (line, "true") || ! strcasecmp (line, "yes")
|| line[0] == '1')
flag = 1;
-
+
return gt_set_textmode (server->gt, flag);
}
else
@@ -2160,12 +2519,12 @@ cmd_include_certs (assuan_context_t ctx, char *line)
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
@@ -2186,7 +2545,7 @@ cmd_keylist_mode (assuan_context_t ctx, char *line)
if (line && *line)
{
gpgme_keylist_mode_t mode = 0;
-
+
if (strstr (line, "local"))
mode |= GPGME_KEYLIST_MODE_LOCAL;
if (strstr (line, "extern"))
@@ -2199,7 +2558,7 @@ cmd_keylist_mode (assuan_context_t ctx, char *line)
mode |= GPGME_KEYLIST_MODE_EPHEMERAL;
if (strstr (line, "validate"))
mode |= GPGME_KEYLIST_MODE_VALIDATE;
-
+
return gt_set_keylist_mode (server->gt, mode);
}
else
@@ -2331,15 +2690,15 @@ _cmd_decrypt_verify (assuan_context_t ctx, char *line, int verify)
gpgme_data_t inp_data;
gpgme_data_t out_data;
- inp_fd = assuan_get_input_fd (ctx);
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
return GPG_ERR_ASS_NO_INPUT;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
if (out_fd == ASSUAN_INVALID_FD && !out_fn)
return GPG_ERR_ASS_NO_OUTPUT;
-
+
err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
&server->input_stream);
if (err)
@@ -2352,7 +2711,7 @@ _cmd_decrypt_verify (assuan_context_t ctx, char *line, int verify)
return err;
}
- err = gt_decrypt_verify (server->gt, inp_data, out_data, verify);
+ err = gt_decrypt_verify (server->gt, inp_data, out_data, verify);
gpgme_data_release (inp_data);
gpgme_data_release (out_data);
@@ -2410,10 +2769,10 @@ _cmd_sign_encrypt (assuan_context_t ctx, char *line, int sign)
flags |= GPGME_ENCRYPT_PREPARE;
if (strstr (line, "--expect-sign"))
flags |= GPGME_ENCRYPT_EXPECT_SIGN;
-
- inp_fd = assuan_get_input_fd (ctx);
+
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
if (inp_fd != ASSUAN_INVALID_FD || inp_fn)
{
@@ -2433,7 +2792,7 @@ _cmd_sign_encrypt (assuan_context_t ctx, char *line, int sign)
}
}
- err = gt_sign_encrypt (server->gt, flags, inp_data, out_data, sign);
+ err = gt_sign_encrypt (server->gt, flags, inp_data, out_data, sign);
gpgme_data_release (inp_data);
gpgme_data_release (out_data);
@@ -2502,15 +2861,15 @@ cmd_sign (assuan_context_t ctx, char *line)
if (strstr (line, "--detach"))
mode = GPGME_SIG_MODE_DETACH;
- inp_fd = assuan_get_input_fd (ctx);
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
return GPG_ERR_ASS_NO_INPUT;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
if (out_fd == ASSUAN_INVALID_FD && !out_fn)
return GPG_ERR_ASS_NO_OUTPUT;
-
+
err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
&server->input_stream);
if (err)
@@ -2555,13 +2914,13 @@ cmd_verify (assuan_context_t ctx, char *line)
gpgme_data_t msg_data = NULL;
gpgme_data_t out_data = NULL;
- inp_fd = assuan_get_input_fd (ctx);
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
return GPG_ERR_ASS_NO_INPUT;
msg_fd = server->message_fd;
msg_fn = server->message_filename;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
@@ -2614,7 +2973,7 @@ 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 };
@@ -2627,8 +2986,8 @@ cmd_import (assuan_context_t ctx, char *line)
assuan_fd_t inp_fd;
char *inp_fn;
gpgme_data_t inp_data;
-
- inp_fd = assuan_get_input_fd (ctx);
+
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
return GPG_ERR_ASS_NO_INPUT;
@@ -2637,9 +2996,9 @@ cmd_import (assuan_context_t ctx, char *line)
&server->input_stream);
if (err)
return err;
-
- err = gt_import (server->gt, inp_data);
-
+
+ err = gt_import (server->gt, inp_data);
+
gpgme_data_release (inp_data);
server_reset_fds (server);
@@ -2648,7 +3007,7 @@ cmd_import (assuan_context_t ctx, char *line)
}
-static const char hlp_export[] =
+static const char hlp_export[] =
"EXPORT [--extern] [--minimal] [<pattern>]\n"
"\n"
"Export the keys described by PATTERN. Write the\n"
@@ -2664,7 +3023,7 @@ cmd_export (assuan_context_t ctx, char *line)
gpgme_export_mode_t mode = 0;
const char *pattern[2];
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
if (out_fd == ASSUAN_INVALID_FD && !out_fn)
return GPG_ERR_ASS_NO_OUTPUT;
@@ -2724,13 +3083,13 @@ cmd_genkey (assuan_context_t ctx, char *line)
gpgme_data_t parms_data = NULL;
const char *parms;
- inp_fd = assuan_get_input_fd (ctx);
+ inp_fd = server->input_fd;
inp_fn = server->input_filename;
if (inp_fd == ASSUAN_INVALID_FD && !inp_fn)
return GPG_ERR_ASS_NO_INPUT;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
-
+
err = server_data_obj (inp_fd, inp_fn, 0, server->input_enc, &inp_data,
&server->input_stream);
if (err)
@@ -2789,7 +3148,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
if (parms_data)
gpgme_data_release (parms_data);
- return err;
+ return err;
}
@@ -2811,7 +3170,7 @@ cmd_delete (assuan_context_t ctx, char *line)
}
-static const char hlp_keylist[] =
+static const char hlp_keylist[] =
"KEYLIST [--secret-only] [<patterns>]\n"
"\n"
"List all certificates or only those specified by PATTERNS. Each\n"
@@ -2821,9 +3180,11 @@ cmd_keylist (assuan_context_t ctx, char *line)
{
#define MAX_CMD_KEYLIST_PATTERN 20
struct server *server = assuan_get_pointer (ctx);
+ gpgme_tool_t gt = server->gt;
+ struct result_xml_state state;
gpg_error_t err;
int secret_only = 0;
- int idx;
+ int idx, indent=2;
const char *pattern[MAX_CMD_KEYLIST_PATTERN+1];
const char optstr[] = "--secret-only";
char *p;
@@ -2853,10 +3214,19 @@ cmd_keylist (assuan_context_t ctx, char *line)
}
pattern[idx] = NULL;
+ gt_write_data (gt, xml_preamble1, sizeof (xml_preamble1));
+ gt_write_data (gt, NULL, 0);
+ gt_write_data (gt, xml_preamble2, sizeof (xml_preamble2));
+ gt_write_data (gt, NULL, 0);
+ result_init (&state, indent, (result_xml_write_cb_t) gt_write_data, gt);
+ result_xml_tag_start (&state, "keylist", NULL);
+
err = gt_keylist_start (server->gt, pattern, secret_only);
while (! err)
{
gpgme_key_t key;
+ gpgme_subkey_t subkey;
+ gpgme_user_id_t uid;
err = gt_keylist_next (server->gt, &key);
if (gpg_err_code (err) == GPG_ERR_EOF)
@@ -2866,25 +3236,62 @@ cmd_keylist (assuan_context_t ctx, char *line)
}
else if (! err)
{
- char buf[100];
- /* FIXME: More data. */
- snprintf (buf, sizeof (buf), "key:%s\n", key->subkeys->fpr);
- /* Write data and flush so that we see one D line for each
- key. This does not change the semantics but is easier to
- read by organic eyes. */
- if (!assuan_send_data (ctx, buf, strlen (buf)))
- assuan_send_data (ctx, NULL, 0);
+ result_xml_tag_start (&state, "key", NULL);
+ result_add_value (&state, "revoked", key->revoked);
+ result_add_value (&state, "expired", key->expired);
+ result_add_value (&state, "disabled", key->disabled);
+ result_add_value (&state, "invalid", key->invalid);
+ result_add_value (&state, "can-encrypt", key->can_encrypt);
+ result_add_value (&state, "can-sign", key->can_sign);
+ result_add_value (&state, "can-certify", key->can_certify);
+ result_add_value (&state, "can-authenticate", key->can_authenticate);
+ result_add_value (&state, "is-qualified", key->is_qualified);
+ result_add_value (&state, "secret", key->secret);
+ result_add_protocol (&state, "protocol", key->protocol);
+ result_xml_tag_start (&state, "issuer", NULL);
+ result_add_string (&state, "serial", key->issuer_serial);
+ result_add_string (&state, "name", key->issuer_name);
+ result_xml_tag_end (&state); /* issuer */
+ result_add_string (&state, "chain-id", key->chain_id);
+ result_add_validity (&state, "owner-trust", key->owner_trust);
+ result_xml_tag_start (&state, "subkeys", NULL);
+ subkey = key->subkeys;
+ while (subkey) {
+ result_xml_tag_start (&state, "subkey", NULL);
+ /* FIXME: more data */
+ result_add_fpr (&state, "fpr", subkey->fpr);
+ result_xml_tag_end (&state); /* subkey */
+ subkey = subkey->next;
+ }
+ result_xml_tag_end (&state); /* subkeys */
+ result_xml_tag_start (&state, "uids", NULL);
+ uid = key->uids;
+ while (uid) {
+ result_xml_tag_start (&state, "uid", NULL);
+ /* FIXME: more data */
+ result_add_string (&state, "uid", uid->uid);
+ result_add_string (&state, "name", uid->name);
+ result_add_string (&state, "email", uid->email);
+ result_add_string (&state, "comment", uid->comment);
+ result_xml_tag_end (&state); /* uid */
+ uid = uid->next;
+ }
+ result_xml_tag_end (&state); /* uids */
+ result_xml_tag_end (&state); /* key */
gpgme_key_unref (key);
}
}
-
+
+ result_xml_tag_end (&state); /* keylist */
+ gt_write_data (gt, xml_end, sizeof (xml_end));
+
server_reset_fds (server);
return err;
}
-static const char hlp_getauditlog[] =
+static const char hlp_getauditlog[] =
"GETAUDITLOG [--html] [--with-help]\n"
"\n"
"Call the function gpgme_op_getauditlog with the given flags. Write\n"
@@ -2899,7 +3306,7 @@ cmd_getauditlog (assuan_context_t ctx, char *line)
gpgme_data_t out_data;
unsigned int flags = 0;
- out_fd = assuan_get_output_fd (ctx);
+ out_fd = server->output_fd;
out_fn = server->output_filename;
if (out_fd == ASSUAN_INVALID_FD && !out_fn)
return GPG_ERR_ASS_NO_OUTPUT;
@@ -3036,6 +3443,7 @@ register_commands (assuan_context_t ctx)
{ "ENGINE", cmd_engine, hlp_engine },
{ "PROTOCOL", cmd_protocol, hlp_protocol },
{ "SUB_PROTOCOL", cmd_sub_protocol, hlp_sub_protocol },
+ { "PINENTRY_MODE", cmd_pinentry_mode, hlp_pinentry_mode },
{ "ARMOR", cmd_armor, hlp_armor },
{ "TEXTMODE", cmd_textmode, hlp_textmode },
{ "INCLUDE_CERTS", cmd_include_certs, hlp_include_certs },
@@ -3087,12 +3495,11 @@ register_commands (assuan_context_t ctx)
table[idx].help);
if (err)
return err;
- }
+ }
return 0;
}
-/* TODO: password callback can do INQUIRE. */
void
gpgme_server (gpgme_tool_t gt)
{
@@ -3102,6 +3509,8 @@ gpgme_server (gpgme_tool_t gt)
static const char hello[] = ("GPGME-Tool " VERSION " ready");
memset (&server, 0, sizeof (server));
+ server.input_fd = ASSUAN_INVALID_FD;
+ server.output_fd = ASSUAN_INVALID_FD;
server.message_fd = ASSUAN_INVALID_FD;
server.input_enc = GPGME_DATA_ENCODING_NONE;
server.output_enc = GPGME_DATA_ENCODING_NONE;
@@ -3153,7 +3562,7 @@ gpgme_server (gpgme_tool_t gt)
log_error (0, err, "assuan accept problem");
break;
}
-
+
err = assuan_process (server.assuan_ctx);
if (err)
log_error (0, err, "assuan processing failed");
@@ -3175,6 +3584,7 @@ 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 }
};
@@ -3184,6 +3594,7 @@ static struct argp argp = { options, parse_options, args_doc, doc };
struct args
{
enum { CMD_DEFAULT, CMD_SERVER } cmd;
+ const char *gpg_binary;
};
void
@@ -3204,6 +3615,10 @@ parse_options (int key, char *arg, struct argp_state *state)
case 's':
args->cmd = CMD_SERVER;
break;
+
+ case 501:
+ args->gpg_binary = arg;
+ break;
#if 0
case ARGP_KEY_ARG:
if (state->arg_num >= 2)
@@ -3228,6 +3643,7 @@ main (int argc, char *argv[])
{
struct args args;
struct gpgme_tool gt;
+ gpg_error_t err;
#ifdef HAVE_SETLOCALE
setlocale (LC_ALL, "");
@@ -3245,6 +3661,18 @@ main (int argc, char *argv[])
argp_parse (&argp, argc, argv, 0, 0, &args);
log_init ();
+ if (args.gpg_binary)
+ {
+ if (access (args.gpg_binary, X_OK))
+ err = gpg_error_from_syserror ();
+ else
+ err = gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP,
+ args.gpg_binary, NULL);
+ if (err)
+ log_error (1, err, "error witching OpenPGP engine to '%s'",
+ args.gpg_binary);
+ }
+
gt_init (&gt);
switch (args.cmd)