diff --git a/src/Makefile.am b/src/Makefile.am index e456d805..ea8e70eb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,237 +1,230 @@ # 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, see . ## Process this file with automake to produce Makefile.in # Note: moc_kdpipeiodevice should actually be a dependcy below. -EXTRA_DIST = gpgme-config.in gpgme.m4 mkstatus libgpgme.vers ChangeLog-2011 \ +EXTRA_DIST = gpgme-config.in gpgme.m4 libgpgme.vers ChangeLog-2011 \ gpgme.h.in versioninfo.rc.in gpgme.def moc_kdpipeiodevice.cpp -BUILT_SOURCES = status-table.h -MOSTLYCLEANFILES = status-table.h bin_SCRIPTS = gpgme-config m4datadir = $(datadir)/aclocal m4data_DATA = gpgme.m4 nodist_include_HEADERS = gpgme.h if HAVE_PTHREAD ltlib_gpgme_pthread = libgpgme-pthread.la else ltlib_gpgme_pthread = endif if BUILD_W32_GLIB ltlib_gpgme_glib = libgpgme-glib.la else ltlib_gpgme_glib = endif if BUILD_W32_QT ltlib_gpgme_qt = libgpgme-qt.la else ltlib_gpgme_qt = endif lib_LTLIBRARIES = libgpgme.la $(ltlib_gpgme_glib) $(ltlib_gpgme_qt) \ $(ltlib_gpgme_pthread) if HAVE_LD_VERSION_SCRIPT libgpgme_version_script_cmd = -Wl,--version-script=$(srcdir)/libgpgme.vers else libgpgme_version_script_cmd = endif if HAVE_DOSISH_SYSTEM system_components = w32-util.c w32-sema.c system_components_not_extra = w32-io.c else system_components = ath.h posix-util.c posix-sema.c posix-io.c system_components_not_extra = endif if HAVE_W32CE_SYSTEM system_components += w32-ce.h w32-ce.c endif if HAVE_GPGSM gpgsm_components = engine-gpgsm.c else gpgsm_components = endif if HAVE_ASSUAN assuan_components = assuan-support.c engine-assuan.c else assuan_components = endif if HAVE_GPGCONF gpgconf_components = engine-gpgconf.c else gpgconf_components = endif if HAVE_G13 g13_components = engine-g13.c else g13_components = endif if HAVE_UISERVER uiserver_components = engine-uiserver.c else uiserver_components = endif # These are the source files common to all library versions. We used # to build a non-installed library for that, but that does not work # correctly on all platforms (in particular, one can not specify the # right linking order with libtool, as the non-installed version has # unresolved symbols to the thread module. main_sources = \ util.h conversion.c get-env.c context.h ops.h \ data.h data.c data-fd.c data-stream.c data-mem.c data-user.c \ data-compat.c \ signers.c sig-notation.c \ wait.c wait-global.c wait-private.c wait-user.c wait.h \ op-support.c \ encrypt.c encrypt-sign.c decrypt.c decrypt-verify.c verify.c \ sign.c passphrase.c progress.c \ key.c keylist.c trust-item.c trustlist.c \ import.c export.c genkey.c delete.c edit.c getauditlog.c \ opassuan.c passwd.c \ - engine.h engine-backend.h engine.c engine-gpg.c status-table.h \ - extra-stati.h \ + engine.h engine-backend.h engine.c engine-gpg.c status-table.c \ $(gpgsm_components) $(assuan_components) $(gpgconf_components) \ $(uiserver_components) \ $(g13_components) vfs-mount.c vfs-create.c \ gpgconf.c \ sema.h priv-io.h $(system_components) dirinfo.c \ debug.c debug.h gpgme.c version.c error.c libgpgme_la_SOURCES = $(main_sources) \ ath.h ath.c $(system_components_not_extra) libgpgme_pthread_la_SOURCES = $(main_sources) \ ath.h ath-pthread.c $(system_components_not_extra) if BUILD_W32_GLIB libgpgme_glib_la_SOURCES = $(main_sources) ath.h ath.c w32-glib-io.c endif if BUILD_W32_QT libgpgme_qt_la_SOURCES = $(main_sources) ath.h ath.c w32-qt-io.cpp \ kdpipeiodevice.h kdpipeiodevice.cpp kdpipeiodevice.moc # FIXME: Add extra depedency: moc_kdpipeiodevice.cpp # These are built sources (normally). # moc_kdpipeiodevice.cpp: kdpipeiodevice.h # $(MOC4) -o $@ $< # # kdpipeiodevice.moc: kdpipeiodevice.cpp # $(MOC4) -o $@ $< endif # We use a global CFLAGS and CPPFLAGS setting for all library # versions, because then every object file is only compiled once. AM_CPPFLAGS = @GPG_ERROR_CFLAGS@ @QT4_CORE_CFLAGS@ AM_CFLAGS = @LIBASSUAN_CFLAGS@ @GLIB_CFLAGS@ @QT4_CORE_CFLAGS@ if HAVE_W32_SYSTEM # Windows provides us with an endless stream of Tough Love. To spawn # processes with a controlled set of inherited handles, we need a # wrapper process. # Except on Windows CE. There nothing is inheritable anyway. if HAVE_W32CE_SYSTEM libexec_PROGRAMS = else libexec_PROGRAMS = gpgme-w32spawn endif RCCOMPILE = $(RC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) LTRCCOMPILE = $(LIBTOOL) --mode=compile --tag=RC $(RCCOMPILE) SUFFIXES = .rc .lo .rc.lo: $(LTRCCOMPILE) -i "$<" -o "$@" gpgme_res = versioninfo.lo no_undefined = -no-undefined export_symbols = -export-symbols $(srcdir)/gpgme.def install-def-file: $(INSTALL) $(srcdir)/gpgme.def $(DESTDIR)$(libdir)/gpgme.def uninstall-def-file: -rm $(DESTDIR)$(libdir)/gpgme.def gpgme_deps = $(gpgme_res) gpgme.def else gpgme_res = no_undefined = export_symbols = install-def-file: uninstall-def-file: gpgme_deps = endif libgpgme_la_LDFLAGS = $(no_undefined) $(export_symbols) \ $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ libgpgme_pthread_la_LDFLAGS = $(no_undefined) $(export_symbols) \ $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_pthread_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers libgpgme_pthread_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ -lpthread @GPG_ERROR_LIBS@ if BUILD_W32_GLIB libgpgme_glib_la_LDFLAGS = $(no_undefined) \ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_glib_la_DEPENDENCIES = @LTLIBOBJS@ \ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_glib_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @GLIB_LIBS@ endif if BUILD_W32_QT libgpgme_qt_la_LDFLAGS = $(no_undefined) \ $(export_symbols) $(libgpgme_version_script_cmd) -version-info \ @LIBGPGME_LT_CURRENT@:@LIBGPGME_LT_REVISION@:@LIBGPGME_LT_AGE@ libgpgme_qt_la_DEPENDENCIES = @LTLIBOBJS@ $(srcdir)/libgpgme.vers $(gpgme_deps) libgpgme_qt_la_LIBADD = $(gpgme_res) @LIBASSUAN_LIBS@ @LTLIBOBJS@ \ @GPG_ERROR_LIBS@ @QT4_CORE_LIBS@ endif noinst_PROGRAMS = gpgme-tool gpgme_tool_LDADD = libgpgme.la @LIBASSUAN_LIBS@ -status-table.h : gpgme.h extra-stati.h - cat $(builddir)/gpgme.h $(srcdir)extra-stati.h \ - | $(srcdir)/mkstatus > status-table.h - install-data-local: install-def-file uninstall-local: uninstall-def-file diff --git a/src/decrypt.c b/src/decrypt.c index b74c5081..43945ec3 100644 --- a/src/decrypt.c +++ b/src/decrypt.c @@ -1,404 +1,403 @@ /* decrypt.c - Decrypt function. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004 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 #endif #include #include #include #include "debug.h" #include "gpgme.h" #include "util.h" #include "context.h" #include "ops.h" -#include "extra-stati.h" typedef struct { struct _gpgme_op_decrypt_result result; int okay; int failed; /* A pointer to the next pointer of the last recipient in the list. This makes appending new invalid signers painless while preserving the order. */ gpgme_recipient_t *last_recipient_p; } *op_data_t; static void release_op_data (void *hook) { op_data_t opd = (op_data_t) hook; gpgme_recipient_t recipient = opd->result.recipients; if (opd->result.unsupported_algorithm) free (opd->result.unsupported_algorithm); if (opd->result.file_name) free (opd->result.file_name); while (recipient) { gpgme_recipient_t next = recipient->next; free (recipient); recipient = next; } } gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx) { void *hook; op_data_t opd; gpgme_error_t err; TRACE_BEG (DEBUG_CTX, "gpgme_op_decrypt_result", ctx); err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err || !opd) { TRACE_SUC0 ("result=(null)"); return NULL; } if (_gpgme_debug_trace ()) { gpgme_recipient_t rcp; if (opd->result.unsupported_algorithm) { TRACE_LOG1 ("result: unsupported_algorithm: %s", opd->result.unsupported_algorithm); } if (opd->result.wrong_key_usage) { TRACE_LOG ("result: wrong key usage"); } rcp = opd->result.recipients; while (rcp) { TRACE_LOG3 ("result: recipient: keyid=%s, pubkey_algo=%i, " "status=%s", rcp->keyid, rcp->pubkey_algo, gpg_strerror (rcp->status)); rcp = rcp->next; } if (opd->result.file_name) { TRACE_LOG1 ("result: original file name: %s", opd->result.file_name); } } TRACE_SUC1 ("result=%p", &opd->result); return &opd->result; } static gpgme_error_t parse_enc_to (char *args, gpgme_recipient_t *recp) { gpgme_recipient_t rec; char *tail; int i; rec = malloc (sizeof (*rec)); if (!rec) return gpg_error_from_syserror (); rec->next = NULL; rec->keyid = rec->_keyid; rec->status = 0; for (i = 0; i < sizeof (rec->_keyid) - 1; i++) { if (args[i] == '\0' || args[i] == ' ') break; rec->_keyid[i] = args[i]; } rec->_keyid[i] = '\0'; args = &args[i]; if (*args != '\0' && *args != ' ') { free (rec); return gpg_error (GPG_ERR_INV_ENGINE); } while (*args == ' ') args++; if (*args) { gpg_err_set_errno (0); rec->pubkey_algo = strtol (args, &tail, 0); if (errno || args == tail || *tail != ' ') { /* The crypto backend does not behave. */ free (rec); return gpg_error (GPG_ERR_INV_ENGINE); } } /* FIXME: The key length is always 0 right now, so no need to parse it. */ *recp = rec; return 0; } gpgme_error_t _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_ctx_t ctx = (gpgme_ctx_t) priv; gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_passphrase_status_handler (priv, code, args); if (err) return err; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, -1, NULL); opd = hook; if (err) return err; switch (code) { case GPGME_STATUS_EOF: /* FIXME: These error values should probably be attributed to the underlying crypto engine (as error source). */ if (opd->failed) return gpg_error (GPG_ERR_DECRYPT_FAILED); else if (!opd->okay) return gpg_error (GPG_ERR_NO_DATA); break; case GPGME_STATUS_DECRYPTION_INFO: /* Fixme: Provide a way to return the used symmetric algorithm. */ break; case GPGME_STATUS_DECRYPTION_OKAY: opd->okay = 1; break; case GPGME_STATUS_DECRYPTION_FAILED: opd->failed = 1; break; case GPGME_STATUS_ERROR: /* Note that this is an informational status code which should not lead to an error return unless it is something not related to the backend. */ { const char d_alg[] = "decrypt.algorithm"; const char k_alg[] = "decrypt.keyusage"; if (!strncmp (args, d_alg, sizeof (d_alg) - 1)) { args += sizeof (d_alg) - 1; while (*args == ' ') args++; if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM) { char *end; while (*args && *args != ' ') args++; while (*args == ' ') args++; end = strchr (args, ' '); if (end) *end = '\0'; if (!(*args == '?' && *(args + 1) == '\0')) { opd->result.unsupported_algorithm = strdup (args); if (!opd->result.unsupported_algorithm) return gpg_error_from_syserror (); } } } else if (!strncmp (args, k_alg, sizeof (k_alg) - 1)) { args += sizeof (k_alg) - 1; while (*args == ' ') args++; if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE) opd->result.wrong_key_usage = 1; } } break; case GPGME_STATUS_ENC_TO: err = parse_enc_to (args, opd->last_recipient_p); if (err) return err; opd->last_recipient_p = &(*opd->last_recipient_p)->next; break; case GPGME_STATUS_NO_SECKEY: { gpgme_recipient_t rec = opd->result.recipients; while (rec) { if (!strcmp (rec->keyid, args)) { rec->status = gpg_error (GPG_ERR_NO_SECKEY); break; } rec = rec->next; } /* FIXME: Is this ok? */ if (!rec) return gpg_error (GPG_ERR_INV_ENGINE); } break; case GPGME_STATUS_PLAINTEXT: err = _gpgme_parse_plaintext (args, &opd->result.file_name); if (err) return err; default: break; } return 0; } static gpgme_error_t decrypt_status_handler (void *priv, gpgme_status_code_t code, char *args) { gpgme_error_t err; err = _gpgme_progress_status_handler (priv, code, args); if (!err) err = _gpgme_decrypt_status_handler (priv, code, args); return err; } gpgme_error_t _gpgme_op_decrypt_init_result (gpgme_ctx_t ctx) { gpgme_error_t err; void *hook; op_data_t opd; err = _gpgme_op_data_lookup (ctx, OPDATA_DECRYPT, &hook, sizeof (*opd), release_op_data); opd = hook; if (err) return err; opd->last_recipient_p = &opd->result.recipients; return 0; } static gpgme_error_t decrypt_start (gpgme_ctx_t ctx, int synchronous, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; err = _gpgme_op_reset (ctx, synchronous); if (err) return err; err = _gpgme_op_decrypt_init_result (ctx); if (err) return err; if (!cipher) return gpg_error (GPG_ERR_NO_DATA); if (!plain) return gpg_error (GPG_ERR_INV_VALUE); if (err) return err; if (ctx->passphrase_cb) { err = _gpgme_engine_set_command_handler (ctx->engine, _gpgme_passphrase_command_handler, ctx, NULL); if (err) return err; } _gpgme_engine_set_status_handler (ctx->engine, decrypt_status_handler, ctx); return _gpgme_engine_op_decrypt (ctx->engine, cipher, plain); } gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt_start", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_start (ctx, 0, cipher, plain); return TRACE_ERR (err); } /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain) { gpgme_error_t err; TRACE_BEG2 (DEBUG_CTX, "gpgme_op_decrypt", ctx, "cipher=%p, plain=%p", cipher, plain); if (!ctx) return TRACE_ERR (gpg_error (GPG_ERR_INV_VALUE)); err = decrypt_start (ctx, 1, cipher, plain); if (!err) err = _gpgme_wait_one (ctx); return TRACE_ERR (err); } diff --git a/src/engine-gpg.c b/src/engine-gpg.c index f163f0fd..cbb456ec 100644 --- a/src/engine-gpg.c +++ b/src/engine-gpg.c @@ -1,2407 +1,2393 @@ /* engine-gpg.c - Gpg Engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, - 2009, 2010 g10 Code GmbH + 2009, 2010, 2012 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, see . */ #if HAVE_CONFIG_H #include #endif #include #include #include #include #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "context.h" /*temp hack until we have GpmeData methods to do I/O */ #include "priv-io.h" #include "sema.h" #include "debug.h" -#include "status-table.h" #include "engine-backend.h" /* This type is used to build a list of gpg arguments and data sources/sinks. */ struct arg_and_data_s { struct arg_and_data_s *next; gpgme_data_t data; /* If this is not NULL, use arg below. */ int inbound; /* True if this is used for reading from gpg. */ int dup_to; int print_fd; /* Print the fd number and not the special form of it. */ int *arg_locp; /* Write back the argv idx of this argument when building command line to this location. */ char arg[1]; /* Used if data above is not used. */ }; struct fd_data_map_s { gpgme_data_t data; int inbound; /* true if this is used for reading from gpg */ int dup_to; int fd; /* the fd to use */ int peer_fd; /* the other side of the pipe */ int arg_loc; /* The index into the argv for translation purposes. */ void *tag; }; typedef gpgme_error_t (*colon_preprocessor_t) (char *line, char **rline); struct engine_gpg { char *file_name; char *lc_messages; char *lc_ctype; struct arg_and_data_s *arglist; struct arg_and_data_s **argtail; struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_status_handler_t fnc; void *fnc_value; void *tag; } status; /* This is a kludge - see the comment at colon_line_handler. */ struct { int fd[2]; int arg_loc; size_t bufsize; char *buffer; size_t readpos; int eof; engine_colon_line_handler_t fnc; /* this indicate use of this structrue */ void *fnc_value; void *tag; colon_preprocessor_t preprocess_fnc; } colon; char **argv; struct fd_data_map_s *fd_data_map; /* stuff needed for interactive (command) mode */ struct { int used; int fd; void *cb_data; int idx; /* Index in fd_data_map */ gpgme_status_code_t code; /* last code */ char *keyword; /* what has been requested (malloced) */ engine_command_handler_t fnc; void *fnc_value; /* The kludges never end. This is used to couple command handlers with output data in edit key mode. */ gpgme_data_t linked_data; int linked_idx; } cmd; struct gpgme_io_cbs io_cbs; }; typedef struct engine_gpg *engine_gpg_t; static void gpg_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpg_t gpg = engine; TRACE3 (DEBUG_ENGINE, "gpgme:gpg_io_event", gpg, "event %p, type %d, type_data %p", gpg->io_cbs.event, type, type_data); if (gpg->io_cbs.event) (*gpg->io_cbs.event) (gpg->io_cbs.event_priv, type, type_data); } static void close_notify_handler (int fd, void *opaque) { engine_gpg_t gpg = opaque; assert (fd != -1); if (gpg->status.fd[0] == fd) { if (gpg->status.tag) (*gpg->io_cbs.remove) (gpg->status.tag); gpg->status.fd[0] = -1; } else if (gpg->status.fd[1] == fd) gpg->status.fd[1] = -1; else if (gpg->colon.fd[0] == fd) { if (gpg->colon.tag) (*gpg->io_cbs.remove) (gpg->colon.tag); gpg->colon.fd[0] = -1; } else if (gpg->colon.fd[1] == fd) gpg->colon.fd[1] = -1; else if (gpg->cmd.fd == fd) gpg->cmd.fd = -1; else if (gpg->fd_data_map) { int i; for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->fd_data_map[i].fd == fd) { if (gpg->fd_data_map[i].tag) (*gpg->io_cbs.remove) (gpg->fd_data_map[i].tag); gpg->fd_data_map[i].fd = -1; break; } if (gpg->fd_data_map[i].peer_fd == fd) { gpg->fd_data_map[i].peer_fd = -1; break; } } } } /* If FRONT is true, push at the front of the list. Use this for options added late in the process. */ static gpgme_error_t _add_arg (engine_gpg_t gpg, const char *arg, int front, int *arg_locp) { struct arg_and_data_s *a; assert (gpg); assert (arg); a = malloc (sizeof *a + strlen (arg)); if (!a) return gpg_error_from_errno (errno); a->data = NULL; a->dup_to = -1; a->arg_locp = arg_locp; strcpy (a->arg, arg); if (front) { a->next = gpg->arglist; if (!gpg->arglist) { /* If this is the first argument, we need to update the tail pointer. */ gpg->argtail = &a->next; } gpg->arglist = a; } else { a->next = NULL; *gpg->argtail = a; gpg->argtail = &a->next; } return 0; } static gpgme_error_t add_arg_ext (engine_gpg_t gpg, const char *arg, int front) { return _add_arg (gpg, arg, front, NULL); } static gpgme_error_t add_arg_with_locp (engine_gpg_t gpg, const char *arg, int *locp) { return _add_arg (gpg, arg, 0, locp); } static gpgme_error_t add_arg (engine_gpg_t gpg, const char *arg) { return add_arg_ext (gpg, arg, 0); } static gpgme_error_t add_data (engine_gpg_t gpg, gpgme_data_t data, int dup_to, int inbound) { struct arg_and_data_s *a; assert (gpg); assert (data); a = malloc (sizeof *a - 1); if (!a) return gpg_error_from_errno (errno); a->next = NULL; a->data = data; a->inbound = inbound; a->arg_locp = NULL; if (dup_to == -2) { a->print_fd = 1; a->dup_to = -1; } else { a->print_fd = 0; a->dup_to = dup_to; } *gpg->argtail = a; gpg->argtail = &a->next; return 0; } static char * gpg_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_gpg_path ()); } static const char * gpg_get_req_version (void) { return NEED_GPG_VERSION; } static void free_argv (char **argv) { int i; for (i = 0; argv[i]; i++) free (argv[i]); free (argv); } static void free_fd_data_map (struct fd_data_map_s *fd_data_map) { int i; if (!fd_data_map) return; for (i = 0; fd_data_map[i].data; i++) { if (fd_data_map[i].fd != -1) _gpgme_io_close (fd_data_map[i].fd); if (fd_data_map[i].peer_fd != -1) _gpgme_io_close (fd_data_map[i].peer_fd); /* Don't release data because this is only a reference. */ } free (fd_data_map); } static gpgme_error_t gpg_cancel (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* If gpg may be waiting for a cmd, close the cmd fd first. On Windows, close operations block on the reader/writer thread. */ if (gpg->cmd.used) { if (gpg->cmd.fd != -1) _gpgme_io_close (gpg->cmd.fd); else if (gpg->fd_data_map && gpg->fd_data_map[gpg->cmd.idx].fd != -1) _gpgme_io_close (gpg->fd_data_map[gpg->cmd.idx].fd); } if (gpg->status.fd[0] != -1) _gpgme_io_close (gpg->status.fd[0]); if (gpg->status.fd[1] != -1) _gpgme_io_close (gpg->status.fd[1]); if (gpg->colon.fd[0] != -1) _gpgme_io_close (gpg->colon.fd[0]); if (gpg->colon.fd[1] != -1) _gpgme_io_close (gpg->colon.fd[1]); if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } return 0; } static void gpg_release (void *engine) { engine_gpg_t gpg = engine; if (!gpg) return; gpg_cancel (engine); if (gpg->file_name) free (gpg->file_name); if (gpg->lc_messages) free (gpg->lc_messages); if (gpg->lc_ctype) free (gpg->lc_ctype); while (gpg->arglist) { struct arg_and_data_s *next = gpg->arglist->next; if (gpg->arglist) free (gpg->arglist); gpg->arglist = next; } if (gpg->status.buffer) free (gpg->status.buffer); if (gpg->colon.buffer) free (gpg->colon.buffer); if (gpg->argv) free_argv (gpg->argv); if (gpg->cmd.keyword) free (gpg->cmd.keyword); free (gpg); } static gpgme_error_t gpg_new (void **engine, const char *file_name, const char *home_dir) { engine_gpg_t gpg; gpgme_error_t rc = 0; char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; gpg = calloc (1, sizeof *gpg); if (!gpg) return gpg_error_from_errno (errno); if (file_name) { gpg->file_name = strdup (file_name); if (!gpg->file_name) { rc = gpg_error_from_errno (errno); goto leave; } } gpg->argtail = &gpg->arglist; gpg->status.fd[0] = -1; gpg->status.fd[1] = -1; gpg->colon.fd[0] = -1; gpg->colon.fd[1] = -1; gpg->cmd.fd = -1; gpg->cmd.idx = -1; gpg->cmd.linked_data = NULL; gpg->cmd.linked_idx = -1; /* Allocate the read buffer for the status pipe. */ gpg->status.bufsize = 1024; gpg->status.readpos = 0; gpg->status.buffer = malloc (gpg->status.bufsize); if (!gpg->status.buffer) { rc = gpg_error_from_errno (errno); goto leave; } /* In any case we need a status pipe - create it right here and don't handle it with our generic gpgme_data_t mechanism. */ if (_gpgme_io_pipe (gpg->status.fd, 1) == -1) { rc = gpg_error_from_errno (errno); goto leave; } if (_gpgme_io_set_close_notify (gpg->status.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->status.fd[1], close_notify_handler, gpg)) { rc = gpg_error (GPG_ERR_GENERAL); goto leave; } gpg->status.eof = 0; if (home_dir) { rc = add_arg (gpg, "--homedir"); if (!rc) rc = add_arg (gpg, home_dir); if (rc) goto leave; } rc = add_arg (gpg, "--status-fd"); if (rc) goto leave; { char buf[25]; _gpgme_io_fd2str (buf, sizeof (buf), gpg->status.fd[1]); rc = add_arg_with_locp (gpg, buf, &gpg->status.arg_loc); if (rc) goto leave; } rc = add_arg (gpg, "--no-tty"); if (!rc) rc = add_arg (gpg, "--charset"); if (!rc) rc = add_arg (gpg, "utf8"); if (!rc) rc = add_arg (gpg, "--enable-progress-filter"); if (rc) goto leave; rc = _gpgme_getenv ("DISPLAY", &dft_display); if (rc) goto leave; if (dft_display) { rc = add_arg (gpg, "--display"); if (!rc) rc = add_arg (gpg, dft_display); free (dft_display); } if (isatty (1)) { int err; err = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (err) rc = gpg_error_from_errno (err); else { if (*dft_ttyname) { rc = add_arg (gpg, "--ttyname"); if (!rc) rc = add_arg (gpg, dft_ttyname); } else rc = 0; if (!rc) { rc = _gpgme_getenv ("TERM", &dft_ttytype); if (rc) goto leave; if (dft_ttytype) { rc = add_arg (gpg, "--ttytype"); if (!rc) rc = add_arg (gpg, dft_ttytype); } free (dft_ttytype); } } if (rc) goto leave; } leave: if (rc) gpg_release (gpg); else *engine = gpg; return rc; } static gpgme_error_t gpg_set_locale (void *engine, int category, const char *value) { engine_gpg_t gpg = engine; if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { if (gpg->lc_ctype) { free (gpg->lc_ctype); gpg->lc_ctype = NULL; } if (value) { gpg->lc_ctype = strdup (value); if (!gpg->lc_ctype) return gpg_error_from_syserror (); } } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { if (gpg->lc_messages) { free (gpg->lc_messages); gpg->lc_messages = NULL; } if (value) { gpg->lc_messages = strdup (value); if (!gpg->lc_messages) return gpg_error_from_syserror (); } } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); return 0; } /* Note, that the status_handler is allowed to modifiy the args value. */ static void gpg_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->status.fnc = fnc; gpg->status.fnc_value = fnc_value; } /* Kludge to process --with-colon output. */ static gpgme_error_t gpg_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpg_t gpg = engine; gpg->colon.bufsize = 1024; gpg->colon.readpos = 0; gpg->colon.buffer = malloc (gpg->colon.bufsize); if (!gpg->colon.buffer) return gpg_error_from_errno (errno); if (_gpgme_io_pipe (gpg->colon.fd, 1) == -1) { int saved_errno = errno; free (gpg->colon.buffer); gpg->colon.buffer = NULL; return gpg_error_from_errno (saved_errno); } if (_gpgme_io_set_close_notify (gpg->colon.fd[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (gpg->colon.fd[1], close_notify_handler, gpg)) return gpg_error (GPG_ERR_GENERAL); gpg->colon.eof = 0; gpg->colon.fnc = fnc; gpg->colon.fnc_value = fnc_value; return 0; } static gpgme_error_t command_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t err; int processed = 0; assert (gpg->cmd.used); assert (gpg->cmd.code); assert (gpg->cmd.fnc); err = gpg->cmd.fnc (gpg->cmd.fnc_value, gpg->cmd.code, gpg->cmd.keyword, fd, &processed); gpg->cmd.code = 0; /* And sleep again until read_status will wake us up again. */ /* XXX We must check if there are any more fds active after removing this one. */ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; gpg->fd_data_map[gpg->cmd.idx].fd = -1; if (err) return err; /* We always need to send at least a newline character. */ if (!processed) _gpgme_io_write (fd, "\n", 1); return 0; } /* The Fnc will be called to get a value for one of the commands with a key KEY. If the Code passed to FNC is 0, the function may release resources associated with the returned value from another call. To match such a second call to a first call, the returned value from the first call is passed as keyword. */ static gpgme_error_t gpg_set_command_handler (void *engine, engine_command_handler_t fnc, void *fnc_value, gpgme_data_t linked_data) { engine_gpg_t gpg = engine; gpgme_error_t rc; rc = add_arg (gpg, "--command-fd"); if (rc) return rc; /* This is a hack. We don't have a real data object. The only thing that matters is that we use something unique, so we use the address of the cmd structure in the gpg object. */ rc = add_data (gpg, (void *) &gpg->cmd, -2, 0); if (rc) return rc; gpg->cmd.fnc = fnc; gpg->cmd.cb_data = (void *) &gpg->cmd; gpg->cmd.fnc_value = fnc_value; gpg->cmd.linked_data = linked_data; gpg->cmd.used = 1; return 0; } static gpgme_error_t build_argv (engine_gpg_t gpg) { gpgme_error_t err; struct arg_and_data_s *a; struct fd_data_map_s *fd_data_map; size_t datac=0, argc=0; char **argv; int need_special = 0; int use_agent = 0; char *p; /* We don't want to use the agent with a malformed environment variable. This is only a very basic test but sufficient to make our life in the regression tests easier. */ err = _gpgme_getenv ("GPG_AGENT_INFO", &p); if (err) return err; use_agent = (p && strchr (p, ':')); if (p) free (p); if (gpg->argv) { free_argv (gpg->argv); gpg->argv = NULL; } if (gpg->fd_data_map) { free_fd_data_map (gpg->fd_data_map); gpg->fd_data_map = NULL; } argc++; /* For argv[0]. */ for (a = gpg->arglist; a; a = a->next) { argc++; if (a->data) { /*fprintf (stderr, "build_argv: data\n" );*/ datac++; if (a->dup_to == -1 && !a->print_fd) need_special = 1; } else { /* fprintf (stderr, "build_argv: arg=`%s'\n", a->arg );*/ } } if (need_special) argc++; if (use_agent) argc++; if (!gpg->cmd.used) argc++; /* --batch */ argc += 1; /* --no-sk-comment */ argv = calloc (argc + 1, sizeof *argv); if (!argv) return gpg_error_from_errno (errno); fd_data_map = calloc (datac + 1, sizeof *fd_data_map); if (!fd_data_map) { int saved_errno = errno; free_argv (argv); return gpg_error_from_errno (saved_errno); } argc = datac = 0; argv[argc] = strdup ("gpg"); /* argv[0] */ if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; if (need_special) { argv[argc] = strdup ("--enable-special-filenames"); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; } if (use_agent) { argv[argc] = strdup ("--use-agent"); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; } if (!gpg->cmd.used) { argv[argc] = strdup ("--batch"); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; } argv[argc] = strdup ("--no-sk-comment"); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; for (a = gpg->arglist; a; a = a->next) { if (a->arg_locp) *(a->arg_locp) = argc; if (a->data) { /* Create a pipe to pass it down to gpg. */ fd_data_map[datac].inbound = a->inbound; /* Create a pipe. */ { int fds[2]; if (_gpgme_io_pipe (fds, fd_data_map[datac].inbound ? 1 : 0) == -1) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error (saved_errno); } if (_gpgme_io_set_close_notify (fds[0], close_notify_handler, gpg) || _gpgme_io_set_close_notify (fds[1], close_notify_handler, gpg)) { return gpg_error (GPG_ERR_GENERAL); } /* If the data_type is FD, we have to do a dup2 here. */ if (fd_data_map[datac].inbound) { fd_data_map[datac].fd = fds[0]; fd_data_map[datac].peer_fd = fds[1]; } else { fd_data_map[datac].fd = fds[1]; fd_data_map[datac].peer_fd = fds[0]; } } /* Hack to get hands on the fd later. */ if (gpg->cmd.used) { if (gpg->cmd.cb_data == a->data) { assert (gpg->cmd.idx == -1); gpg->cmd.idx = datac; } else if (gpg->cmd.linked_data == a->data) { assert (gpg->cmd.linked_idx == -1); gpg->cmd.linked_idx = datac; } } fd_data_map[datac].data = a->data; fd_data_map[datac].dup_to = a->dup_to; if (a->dup_to == -1) { char *ptr; int buflen = 25; argv[argc] = malloc (buflen); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } ptr = argv[argc]; if (!a->print_fd) { *(ptr++) = '-'; *(ptr++) = '&'; buflen -= 2; } _gpgme_io_fd2str (ptr, buflen, fd_data_map[datac].peer_fd); fd_data_map[datac].arg_loc = argc; argc++; } datac++; } else { argv[argc] = strdup (a->arg); if (!argv[argc]) { int saved_errno = errno; free (fd_data_map); free_argv (argv); return gpg_error_from_errno (saved_errno); } argc++; } } gpg->argv = argv; gpg->fd_data_map = fd_data_map; return 0; } static gpgme_error_t add_io_cb (engine_gpg_t gpg, int fd, int dir, gpgme_io_cb_t handler, void *data, void **tag) { gpgme_error_t err; err = (*gpg->io_cbs.add) (gpg->io_cbs.add_priv, fd, dir, handler, data, tag); if (err) return err; if (!dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (fd); return err; } -static int -status_cmp (const void *ap, const void *bp) -{ - const struct status_table_s *a = ap; - const struct status_table_s *b = bp; - - return strcmp (a->name, b->name); -} - - /* Handle the status output of GnuPG. This function does read entire lines and passes them as C strings to the callback function (we can use C Strings because the status output is always UTF-8 encoded). Of course we have to buffer the lines to cope with long lines e.g. with a large user ID. Note: We can optimize this to only cope with status line code we know about and skip all other stuff without buffering (i.e. without extending the buffer). */ static gpgme_error_t read_status (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->status.bufsize; char *buffer = gpg->status.buffer; size_t readpos = gpg->status.readpos; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_errno (errno); } nread = _gpgme_io_read (gpg->status.fd[0], buffer + readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_errno (errno); if (!nread) { gpg->status.eof = 1; if (gpg->status.fnc) { gpgme_error_t err; err = gpg->status.fnc (gpg->status.fnc_value, GPGME_STATUS_EOF, ""); if (err) return err; } return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if (*p == '\n') { /* (we require that the last line is terminated by a LF) */ if (p > buffer && p[-1] == '\r') p[-1] = 0; *p = 0; if (!strncmp (buffer, "[GNUPG:] ", 9) && buffer[9] >= 'A' && buffer[9] <= 'Z') { - struct status_table_s t, *r; char *rest; + gpgme_status_code_t r; rest = strchr (buffer + 9, ' '); if (!rest) rest = p; /* Set to an empty string. */ else *rest++ = 0; - t.name = buffer+9; - /* (the status table has one extra element) */ - r = bsearch (&t, status_table, DIM(status_table) - 1, - sizeof t, status_cmp); - if (r) + r = _gpgme_parse_status (buffer + 9); + if (r >= 0) { if (gpg->cmd.used - && (r->code == GPGME_STATUS_GET_BOOL - || r->code == GPGME_STATUS_GET_LINE - || r->code == GPGME_STATUS_GET_HIDDEN)) + && (r == GPGME_STATUS_GET_BOOL + || r == GPGME_STATUS_GET_LINE + || r == GPGME_STATUS_GET_HIDDEN)) { - gpg->cmd.code = r->code; + gpg->cmd.code = r; if (gpg->cmd.keyword) free (gpg->cmd.keyword); gpg->cmd.keyword = strdup (rest); if (!gpg->cmd.keyword) return gpg_error_from_errno (errno); /* This should be the last thing we have received and the next thing will be that the command handler does its action. */ if (nread > 1) TRACE0 (DEBUG_CTX, "gpgme:read_status", 0, "error: unexpected data"); add_io_cb (gpg, gpg->cmd.fd, 0, command_handler, gpg, &gpg->fd_data_map[gpg->cmd.idx].tag); gpg->fd_data_map[gpg->cmd.idx].fd = gpg->cmd.fd; gpg->cmd.fd = -1; } else if (gpg->status.fnc) { gpgme_error_t err; err = gpg->status.fnc (gpg->status.fnc_value, - r->code, rest); + r, rest); if (err) return err; } - if (r->code == GPGME_STATUS_END_STREAM) + if (r == GPGME_STATUS_END_STREAM) { if (gpg->cmd.used) { /* Before we can actually add the command fd, we might have to flush the linked output data pipe. */ if (gpg->cmd.linked_idx != -1 && gpg->fd_data_map[gpg->cmd.linked_idx].fd != -1) { struct io_select_fd_s fds; fds.fd = gpg->fd_data_map[gpg->cmd.linked_idx].fd; fds.for_read = 1; fds.for_write = 0; fds.opaque = NULL; do { fds.signaled = 0; _gpgme_io_select (&fds, 1, 1); if (fds.signaled) _gpgme_data_inbound_handler (gpg->cmd.linked_data, fds.fd); } while (fds.signaled); } /* XXX We must check if there are any more fds active after removing this one. */ (*gpg->io_cbs.remove) (gpg->fd_data_map[gpg->cmd.idx].tag); gpg->cmd.fd = gpg->fd_data_map[gpg->cmd.idx].fd; gpg->fd_data_map[gpg->cmd.idx].fd = -1; } } } } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* the for loop */ } else readpos++; } } /* Update the gpg object. */ gpg->status.bufsize = bufsize; gpg->status.buffer = buffer; gpg->status.readpos = readpos; return 0; } static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; int err; assert (fd == gpg->status.fd[0]); err = read_status (gpg); if (err) return err; if (gpg->status.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t read_colon_line (engine_gpg_t gpg) { char *p; int nread; size_t bufsize = gpg->colon.bufsize; char *buffer = gpg->colon.buffer; size_t readpos = gpg->colon.readpos; assert (buffer); if (bufsize - readpos < 256) { /* Need more room for the read. */ bufsize += 1024; buffer = realloc (buffer, bufsize); if (!buffer) return gpg_error_from_errno (errno); } nread = _gpgme_io_read (gpg->colon.fd[0], buffer+readpos, bufsize-readpos); if (nread == -1) return gpg_error_from_errno (errno); if (!nread) { gpg->colon.eof = 1; assert (gpg->colon.fnc); gpg->colon.fnc (gpg->colon.fnc_value, NULL); return 0; } while (nread > 0) { for (p = buffer + readpos; nread; nread--, p++) { if ( *p == '\n' ) { /* (we require that the last line is terminated by a LF) and we skip empty lines. Note: we use UTF8 encoding and escaping of special characters. We require at least one colon to cope with some other printed information. */ *p = 0; if (*buffer && strchr (buffer, ':')) { char *line = NULL; if (gpg->colon.preprocess_fnc) { gpgme_error_t err; err = gpg->colon.preprocess_fnc (buffer, &line); if (err) return err; } assert (gpg->colon.fnc); gpg->colon.fnc (gpg->colon.fnc_value, line ? line : buffer); if (line) free (line); } /* To reuse the buffer for the next line we have to shift the remaining data to the buffer start and restart the loop Hmmm: We can optimize this function by looking forward in the buffer to see whether a second complete line is available and in this case avoid the memmove for this line. */ nread--; p++; if (nread) memmove (buffer, p, nread); readpos = 0; break; /* The for loop. */ } else readpos++; } } /* Update the gpg object. */ gpg->colon.bufsize = bufsize; gpg->colon.buffer = buffer; gpg->colon.readpos = readpos; return 0; } /* This colonline handler thing is not the clean way to do it. It might be better to enhance the gpgme_data_t object to act as a wrapper for a callback. Same goes for the status thing. For now we use this thing here because it is easier to implement. */ static gpgme_error_t colon_line_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpg_t gpg = (engine_gpg_t) data->handler_value; gpgme_error_t rc = 0; assert (fd == gpg->colon.fd[0]); rc = read_colon_line (gpg); if (rc) return rc; if (gpg->colon.eof) _gpgme_io_close (fd); return 0; } static gpgme_error_t start (engine_gpg_t gpg) { gpgme_error_t rc; int saved_errno; int i, n; int status; struct spawn_fd_item_s *fd_list; pid_t pid; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); if (!gpg->file_name && !_gpgme_get_gpg_path ()) return gpg_error (GPG_ERR_INV_ENGINE); if (gpg->lc_ctype) { rc = add_arg_ext (gpg, gpg->lc_ctype, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-ctype", 1); if (rc) return rc; } if (gpg->lc_messages) { rc = add_arg_ext (gpg, gpg->lc_messages, 1); if (!rc) rc = add_arg_ext (gpg, "--lc-messages", 1); if (rc) return rc; } rc = build_argv (gpg); if (rc) return rc; /* status_fd, colon_fd and end of list. */ n = 3; for (i = 0; gpg->fd_data_map[i].data; i++) n++; fd_list = calloc (n, sizeof *fd_list); if (! fd_list) return gpg_error_from_errno (errno); /* Build the fd list for the child. */ n = 0; fd_list[n].fd = gpg->status.fd[1]; fd_list[n].dup_to = -1; fd_list[n].arg_loc = gpg->status.arg_loc; n++; if (gpg->colon.fnc) { fd_list[n].fd = gpg->colon.fd[1]; fd_list[n].dup_to = 1; n++; } for (i = 0; gpg->fd_data_map[i].data; i++) { fd_list[n].fd = gpg->fd_data_map[i].peer_fd; fd_list[n].dup_to = gpg->fd_data_map[i].dup_to; fd_list[n].arg_loc = gpg->fd_data_map[i].arg_loc; n++; } fd_list[n].fd = -1; fd_list[n].dup_to = -1; status = _gpgme_io_spawn (gpg->file_name ? gpg->file_name : _gpgme_get_gpg_path (), gpg->argv, IOSPAWN_FLAG_ALLOW_SET_FG, fd_list, NULL, NULL, &pid); saved_errno = errno; free (fd_list); if (status == -1) return gpg_error_from_errno (saved_errno); /*_gpgme_register_term_handler ( closure, closure_value, pid );*/ rc = add_io_cb (gpg, gpg->status.fd[0], 1, status_handler, gpg, &gpg->status.tag); if (rc) /* FIXME: kill the child */ return rc; if (gpg->colon.fnc) { assert (gpg->colon.fd[0] != -1); rc = add_io_cb (gpg, gpg->colon.fd[0], 1, colon_line_handler, gpg, &gpg->colon.tag); if (rc) /* FIXME: kill the child */ return rc; } for (i = 0; gpg->fd_data_map[i].data; i++) { if (gpg->cmd.used && i == gpg->cmd.idx) { /* Park the cmd fd. */ gpg->cmd.fd = gpg->fd_data_map[i].fd; gpg->fd_data_map[i].fd = -1; } else { rc = add_io_cb (gpg, gpg->fd_data_map[i].fd, gpg->fd_data_map[i].inbound, gpg->fd_data_map[i].inbound ? _gpgme_data_inbound_handler : _gpgme_data_outbound_handler, gpg->fd_data_map[i].data, &gpg->fd_data_map[i].tag); if (rc) /* FIXME: kill the child */ return rc; } } gpg_io_event (gpg, GPGME_EVENT_START, NULL); /* fixme: check what data we can release here */ return 0; } static gpgme_error_t gpg_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--decrypt"); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, plain, 1, 1); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, ciph, -1, 0); if (!err) start (gpg); return err; } static gpgme_error_t gpg_delete (void *engine, gpgme_key_t key, int allow_secret) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, allow_secret ? "--delete-secret-and-public-key" : "--delete-key"); if (!err) err = add_arg (gpg, "--"); if (!err) { if (!key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, key->subkeys->fpr); } if (!err) start (gpg); return err; } static gpgme_error_t gpg_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpg_t gpg = engine; gpgme_error_t err; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); err = add_arg (gpg, "--passwd"); if (!err) err = add_arg (gpg, key->subkeys->fpr); if (!err) start (gpg); return err; } static gpgme_error_t append_args_from_signers (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; int i; gpgme_key_t key; for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->keyid : NULL; if (s) { if (!err) err = add_arg (gpg, "-u"); if (!err) err = add_arg (gpg, s); } gpgme_key_unref (key); if (err) break; } return err; } static gpgme_error_t append_args_from_sig_notations (engine_gpg_t gpg, gpgme_ctx_t ctx /* FIXME */) { gpgme_error_t err = 0; gpgme_sig_notation_t notation; notation = gpgme_sig_notation_get (ctx); while (!err && notation) { if (notation->name && !(notation->flags & GPGME_SIG_NOTATION_HUMAN_READABLE)) err = gpg_error (GPG_ERR_INV_VALUE); else if (notation->name) { char *arg; /* Maximum space needed is one byte for the "critical" flag, the name, one byte for '=', the value, and a terminating '\0'. */ arg = malloc (1 + notation->name_len + 1 + notation->value_len + 1); if (!arg) err = gpg_error_from_errno (errno); if (!err) { char *argp = arg; if (notation->critical) *(argp++) = '!'; memcpy (argp, notation->name, notation->name_len); argp += notation->name_len; *(argp++) = '='; /* We know that notation->name is '\0' terminated. */ strcpy (argp, notation->value); } if (!err) err = add_arg (gpg, "--sig-notation"); if (!err) err = add_arg (gpg, arg); if (arg) free (arg); } else { /* This is a policy URL. */ char *value; if (notation->critical) { value = malloc (1 + notation->value_len + 1); if (!value) err = gpg_error_from_errno (errno); else { value[0] = '!'; /* We know that notation->value is '\0' terminated. */ strcpy (&value[1], notation->value); } } else value = notation->value; if (!err) err = add_arg (gpg, "--sig-policy-url"); if (!err) err = add_arg (gpg, value); if (value != notation->value) free (value); } notation = notation->next; } return err; } static gpgme_error_t gpg_edit (void *engine, int type, gpgme_key_t key, gpgme_data_t out, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = add_arg (gpg, type == 0 ? "--edit-key" : "--card-edit"); if (!err) err = add_data (gpg, out, 1, 1); if (!err) err = add_arg (gpg, "--"); if (!err && type == 0) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (!s) err = gpg_error (GPG_ERR_INV_VALUE); else err = add_arg (gpg, s); } if (!err) err = start (gpg); return err; } static gpgme_error_t append_args_from_recipients (engine_gpg_t gpg, gpgme_key_t recp[]) { gpgme_error_t err = 0; int i = 0; while (recp[i]) { if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) err = gpg_error (GPG_ERR_INV_VALUE); if (!err) err = add_arg (gpg, "-r"); if (!err) err = add_arg (gpg, recp[i]->subkeys->fpr); if (err) break; i++; } return err; } static gpgme_error_t gpg_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; int symmetric = !recp; err = add_arg (gpg, symmetric ? "--symmetric" : "--encrypt"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!symmetric) { /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err && (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO)) err = add_arg (gpg, "--no-encrypt-to"); if (!err) err = append_args_from_recipients (gpg, recp); } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_encrypt_sign (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--encrypt"); if (!err) err = add_arg (gpg, "--sign"); if (!err && use_armor) err = add_arg (gpg, "--armor"); /* If we know that all recipients are valid (full or ultimate trust) we can suppress further checks. */ if (!err && (flags & GPGME_ENCRYPT_ALWAYS_TRUST)) err = add_arg (gpg, "--always-trust"); if (!err) err = append_args_from_recipients (gpg, recp); if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_data (gpg, ciph, 1, 1); if (gpgme_data_get_file_name (plain)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (plain)); } if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, plain, -1, 0); if (!err) err = start (gpg); return err; } static gpgme_error_t export_common (engine_gpg_t gpg, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { gpgme_error_t err = 0; if ((mode & ~(GPGME_EXPORT_MODE_EXTERN |GPGME_EXPORT_MODE_MINIMAL))) return gpg_error (GPG_ERR_NOT_SUPPORTED); if ((mode & GPGME_EXPORT_MODE_MINIMAL)) err = add_arg (gpg, "--export-options=export-minimal"); if (err) ; else if ((mode & GPGME_EXPORT_MODE_EXTERN)) { err = add_arg (gpg, "--send-keys"); } else { err = add_arg (gpg, "--export"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) err = add_data (gpg, keydata, 1, 1); } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpg_t gpg = engine; gpgme_error_t err; err = export_common (gpg, mode, keydata, use_armor); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_genkey (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpg_t gpg = engine; gpgme_error_t err; if (!gpg) return gpg_error (GPG_ERR_INV_VALUE); /* We need a special mechanism to get the fd of a pipe here, so that we can use this for the %pubring and %secring parameters. We don't have this yet, so we implement only the adding to the standard keyrings. */ if (pubkey || seckey) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); err = add_arg (gpg, "--gen-key"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, help_data, -1, 0); if (!err) err = start (gpg); return err; } /* Return the next DELIM delimited string from DATA as a C-string. The caller needs to provide the address of a pointer variable which he has to set to NULL before the first call. After the last call to this function, this function needs to be called once more with DATA set to NULL so that the function can release its internal state. After that the pointer variable is free for use again. Note that we use a delimiter and thus a trailing delimiter is not required. DELIM may not be changed after the first call. */ static const char * string_from_data (gpgme_data_t data, int delim, void **helpptr, gpgme_error_t *r_err) { #define MYBUFLEN 2000 /* Fixme: We don't support URLs longer than that. */ struct { int eof_seen; int nbytes; /* Length of the last returned string including the delimiter. */ int buflen; /* Valid length of BUF. */ char buf[MYBUFLEN+1]; /* Buffer with one byte extra space. */ } *self; char *p; int nread; *r_err = 0; if (!data) { if (*helpptr) { free (*helpptr); *helpptr = NULL; } return NULL; } if (*helpptr) self = *helpptr; else { self = malloc (sizeof *self); if (!self) { *r_err = gpg_error_from_syserror (); return NULL; } *helpptr = self; self->eof_seen = 0; self->nbytes = 0; self->buflen = 0; } if (self->eof_seen) return NULL; assert (self->nbytes <= self->buflen); memmove (self->buf, self->buf + self->nbytes, self->buflen - self->nbytes); self->buflen -= self->nbytes; self->nbytes = 0; do { /* Fixme: This is fairly infective scanning because we may scan the buffer several times. */ p = memchr (self->buf, delim, self->buflen); if (p) { *p = 0; self->nbytes = p - self->buf + 1; return self->buf; } if ( !(MYBUFLEN - self->buflen) ) { /* Not enough space - URL too long. */ *r_err = gpg_error (GPG_ERR_TOO_LARGE); return NULL; } nread = gpgme_data_read (data, self->buf + self->buflen, MYBUFLEN - self->buflen); if (nread < 0) { *r_err = gpg_error_from_syserror (); return NULL; } self->buflen += nread; } while (nread); /* EOF reached. If we have anything in the buffer, append a Nul and return it. */ self->eof_seen = 1; if (self->buflen) { self->buf[self->buflen] = 0; /* (we allocated one extra byte) */ return self->buf; } return NULL; #undef MYBUFLEN } static gpgme_error_t gpg_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpg_t gpg = engine; gpgme_error_t err; int idx; gpgme_data_encoding_t dataenc; if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { err = add_arg (gpg, "--recv-keys"); if (!err) err = add_arg (gpg, "--"); for (idx=0; !err && keyarray[idx]; idx++) { if (keyarray[idx]->protocol != GPGME_PROTOCOL_OpenPGP) ; else if (!keyarray[idx]->subkeys) ; else if (keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) err = add_arg (gpg, keyarray[idx]->subkeys->fpr); else if (*keyarray[idx]->subkeys->keyid) err = add_arg (gpg, keyarray[idx]->subkeys->keyid); } } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0) { void *helpptr; const char *string; gpgme_error_t xerr; int delim = (dataenc == GPGME_DATA_ENCODING_URL)? '\n': 0; /* FIXME: --fetch-keys is probably not correct because it can't grok all kinds of URLs. On Unix it should just work but on Windows we will build the command line and that may fail for some embedded control characters. It is anyway limited to the maximum size of the command line. We need another command which can take its input from a file. Maybe we should use an option to gpg to modify such commands (ala --multifile). */ err = add_arg (gpg, "--fetch-keys"); if (!err) err = add_arg (gpg, "--"); helpptr = NULL; while (!err && (string = string_from_data (keydata, delim, &helpptr, &xerr))) err = add_arg (gpg, string); if (!err) err = xerr; string_from_data (NULL, delim, &helpptr, &xerr); } else if (dataenc == GPGME_DATA_ENCODING_URLESC) { /* Already escaped URLs are not yet supported. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { err = add_arg (gpg, "--import"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, keydata, -1, 0); } if (!err) err = start (gpg); return err; } /* The output for external keylistings in GnuPG is different from all the other key listings. We catch this here with a special preprocessor that reformats the colon handler lines. */ static gpgme_error_t gpg_keylist_preprocess (char *line, char **r_line) { enum { RT_NONE, RT_INFO, RT_PUB, RT_UID } rectype = RT_NONE; #define NR_FIELDS 16 char *field[NR_FIELDS]; int fields = 0; *r_line = NULL; while (line && fields < NR_FIELDS) { field[fields++] = line; line = strchr (line, ':'); if (line) *(line++) = '\0'; } if (!strcmp (field[0], "info")) rectype = RT_INFO; else if (!strcmp (field[0], "pub")) rectype = RT_PUB; else if (!strcmp (field[0], "uid")) rectype = RT_UID; else rectype = RT_NONE; switch (rectype) { case RT_INFO: /* FIXME: Eventually, check the version number at least. */ return 0; case RT_PUB: if (fields < 7) return 0; /* The format is: pub:::::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). We want: pub:o::::::::::::: */ if (asprintf (r_line, "pub:o%s:%s:%s:%s:%s:%s::::::::", field[6], field[3], field[2], field[1], field[4], field[5]) < 0) return gpg_error_from_errno (errno); return 0; case RT_UID: /* The format is: uid:::: as defined in 5.2. Machine Readable Indexes of the OpenPGP HTTP Keyserver Protocol (draft). We want: uid:o::::::::: */ { /* The user ID is percent escaped, but we want c-coded. Because we have to replace each '%HL' by '\xHL', we need at most 4/3 th the number of bytes. But because we also need to escape the backslashes we allocate twice as much. */ char *uid = malloc (2 * strlen (field[1]) + 1); char *src; char *dst; if (! uid) return gpg_error_from_errno (errno); src = field[1]; dst = uid; while (*src) { if (*src == '%') { *(dst++) = '\\'; *(dst++) = 'x'; src++; /* Copy the next two bytes unconditionally. */ if (*src) *(dst++) = *(src++); if (*src) *(dst++) = *(src++); } else if (*src == '\\') { *dst++ = '\\'; *dst++ = '\\'; } else *(dst++) = *(src++); } *dst = '\0'; if (asprintf (r_line, "uid:o%s::::%s:%s:::%s:", field[4], field[2], field[3], uid) < 0) return gpg_error_from_errno (errno); } return 0; case RT_NONE: /* Unknown record. */ break; } return 0; } static gpg_error_t gpg_keylist_build_options (engine_gpg_t gpg, int secret_only, gpgme_keylist_mode_t mode) { gpg_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--fixed-list-mode"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err) err = add_arg (gpg, "--with-fingerprint"); if (!err && (mode & GPGME_KEYLIST_MODE_SIGS) && (mode & GPGME_KEYLIST_MODE_SIG_NOTATIONS)) { err = add_arg (gpg, "--list-options"); if (!err) err = add_arg (gpg, "show-sig-subpackets=\"20,26\""); } if (!err) { if ( (mode & GPGME_KEYLIST_MODE_EXTERN) ) { if (secret_only) err = gpg_error (GPG_ERR_NOT_SUPPORTED); else if ( (mode & GPGME_KEYLIST_MODE_LOCAL)) { /* The local+extern mode is special. It works only with gpg >= 2.0.10. FIXME: We should check that we have such a version to that we can return a proper error code. The problem is that we don't know the context here and thus can't access the cached version number for the engine info structure. */ err = add_arg (gpg, "--locate-keys"); if ((mode & GPGME_KEYLIST_MODE_SIGS)) err = add_arg (gpg, "--with-sig-check"); } else { err = add_arg (gpg, "--search-keys"); gpg->colon.preprocess_fnc = gpg_keylist_preprocess; } } else { err = add_arg (gpg, secret_only ? "--list-secret-keys" : ((mode & GPGME_KEYLIST_MODE_SIGS) ? "--check-sigs" : "--list-keys")); } } if (!err) err = add_arg (gpg, "--"); return err; } static gpgme_error_t gpg_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpg_t gpg = engine; gpgme_error_t err; err = gpg_keylist_build_options (gpg, secret_only, mode); if (!err && pattern && *pattern) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { engine_gpg_t gpg = engine; gpgme_error_t err; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); err = gpg_keylist_build_options (gpg, secret_only, mode); if (pattern) { while (!err && *pattern && **pattern) err = add_arg (gpg, *(pattern++)); } if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpg_t gpg = engine; gpgme_error_t err; if (mode == GPGME_SIG_MODE_CLEAR) err = add_arg (gpg, "--clearsign"); else { err = add_arg (gpg, "--sign"); if (!err && mode == GPGME_SIG_MODE_DETACH) err = add_arg (gpg, "--detach"); if (!err && use_armor) err = add_arg (gpg, "--armor"); if (!err && use_textmode) err = add_arg (gpg, "--textmode"); } if (!err) err = append_args_from_signers (gpg, ctx); if (!err) err = append_args_from_sig_notations (gpg, ctx); if (gpgme_data_get_file_name (in)) { if (!err) err = add_arg (gpg, "--set-filename"); if (!err) err = add_arg (gpg, gpgme_data_get_file_name (in)); } /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, in, -1, 0); if (!err) err = add_data (gpg, out, 1, 1); if (!err) start (gpg); return err; } static gpgme_error_t gpg_trustlist (void *engine, const char *pattern) { engine_gpg_t gpg = engine; gpgme_error_t err; err = add_arg (gpg, "--with-colons"); if (!err) err = add_arg (gpg, "--list-trust-path"); /* Tell the gpg object about the data. */ if (!err) err = add_arg (gpg, "--"); if (!err) err = add_arg (gpg, pattern); if (!err) err = start (gpg); return err; } static gpgme_error_t gpg_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { engine_gpg_t gpg = engine; gpgme_error_t err = 0; if (plaintext) { /* Normal or cleartext signature. */ err = add_arg (gpg, "--output"); if (!err) err = add_arg (gpg, "-"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err) err = add_data (gpg, plaintext, 1, 1); } else { err = add_arg (gpg, "--verify"); if (!err) err = add_arg (gpg, "--"); if (!err) err = add_data (gpg, sig, -1, 0); if (!err && signed_text) err = add_data (gpg, signed_text, -1, 0); } if (!err) err = start (gpg); return err; } static void gpg_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpg_t gpg = engine; gpg->io_cbs = *io_cbs; } struct engine_ops _gpgme_engine_ops_gpg = { /* Static functions. */ _gpgme_get_gpg_path, NULL, gpg_get_version, gpg_get_req_version, gpg_new, /* Member functions. */ gpg_release, NULL, /* reset */ gpg_set_status_handler, gpg_set_command_handler, gpg_set_colon_line_handler, gpg_set_locale, NULL, /* set_protocol */ gpg_decrypt, gpg_decrypt, /* decrypt_verify */ gpg_delete, gpg_edit, gpg_encrypt, gpg_encrypt_sign, gpg_export, gpg_export_ext, gpg_genkey, gpg_import, gpg_keylist, gpg_keylist_ext, gpg_sign, gpg_trustlist, gpg_verify, NULL, /* getauditlog */ NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ gpg_set_io_cbs, gpg_io_event, gpg_cancel, NULL, /* cancel_op */ gpg_passwd }; diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c index 4b7b5a2e..0620458a 100644 --- a/src/engine-gpgsm.c +++ b/src/engine-gpgsm.c @@ -1,2014 +1,1989 @@ /* engine-gpgsm.c - GpgSM engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009, 2010 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 #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #ifdef HAVE_LOCALE_H #include #endif #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "data.h" #include "assuan.h" -#include "status-table.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_gpgsm { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; iocb_data_t status_cb; /* Input, output etc are from the servers perspective. */ iocb_data_t input_cb; gpgme_data_t input_helper_data; /* Input helper data object. */ void *input_helper_memory; /* Input helper memory block. */ iocb_data_t output_cb; iocb_data_t message_cb; struct { engine_status_handler_t fnc; void *fnc_value; } status; struct { engine_colon_line_handler_t fnc; void *fnc_value; struct { char *line; int linesize; int linelen; } attic; int any; /* any data line seen */ } colon; gpgme_data_t inline_data; /* Used to collect D lines. */ struct gpgme_io_cbs io_cbs; }; typedef struct engine_gpgsm *engine_gpgsm_t; static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * gpgsm_get_version (const char *file_name) { return _gpgme_get_program_version (file_name ? file_name : _gpgme_get_gpgsm_path ()); } static const char * gpgsm_get_req_version (void) { return NEED_GPGSM_VERSION; } static void close_notify_handler (int fd, void *opaque) { engine_gpgsm_t gpgsm = opaque; assert (fd != -1); if (gpgsm->status_cb.fd == fd) { if (gpgsm->status_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->status_cb.tag); gpgsm->status_cb.fd = -1; gpgsm->status_cb.tag = NULL; } else if (gpgsm->input_cb.fd == fd) { if (gpgsm->input_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->input_cb.tag); gpgsm->input_cb.fd = -1; gpgsm->input_cb.tag = NULL; if (gpgsm->input_helper_data) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; } if (gpgsm->input_helper_memory) { free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; } } else if (gpgsm->output_cb.fd == fd) { if (gpgsm->output_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->output_cb.tag); gpgsm->output_cb.fd = -1; gpgsm->output_cb.tag = NULL; } else if (gpgsm->message_cb.fd == fd) { if (gpgsm->message_cb.tag) (*gpgsm->io_cbs.remove) (gpgsm->message_cb.tag); gpgsm->message_cb.fd = -1; gpgsm->message_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_gpgsm_t gpgsm, const char *line) { if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); } return 0; } static gpgme_error_t gpgsm_cancel (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (gpgsm->status_cb.fd != -1) _gpgme_io_close (gpgsm->status_cb.fd); if (gpgsm->input_cb.fd != -1) _gpgme_io_close (gpgsm->input_cb.fd); if (gpgsm->output_cb.fd != -1) _gpgme_io_close (gpgsm->output_cb.fd); if (gpgsm->message_cb.fd != -1) _gpgme_io_close (gpgsm->message_cb.fd); if (gpgsm->assuan_ctx) { assuan_release (gpgsm->assuan_ctx); gpgsm->assuan_ctx = NULL; } return 0; } static void gpgsm_release (void *engine) { engine_gpgsm_t gpgsm = engine; if (!gpgsm) return; gpgsm_cancel (engine); free (gpgsm->colon.attic.line); free (gpgsm); } static gpgme_error_t gpgsm_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_gpgsm_t gpgsm; const char *argv[5]; int argc; #if !USE_DESCRIPTOR_PASSING int fds[2]; int child_fds[4]; #endif char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; gpgsm = calloc (1, sizeof *gpgsm); if (!gpgsm) return gpg_error_from_syserror (); gpgsm->status_cb.fd = -1; gpgsm->status_cb.dir = 1; gpgsm->status_cb.tag = 0; gpgsm->status_cb.data = gpgsm; gpgsm->input_cb.fd = -1; gpgsm->input_cb.dir = 0; gpgsm->input_cb.tag = 0; gpgsm->input_cb.server_fd = -1; *gpgsm->input_cb.server_fd_str = 0; gpgsm->output_cb.fd = -1; gpgsm->output_cb.dir = 1; gpgsm->output_cb.tag = 0; gpgsm->output_cb.server_fd = -1; *gpgsm->output_cb.server_fd_str = 0; gpgsm->message_cb.fd = -1; gpgsm->message_cb.dir = 0; gpgsm->message_cb.tag = 0; gpgsm->message_cb.server_fd = -1; *gpgsm->message_cb.server_fd_str = 0; gpgsm->status.fnc = 0; gpgsm->colon.fnc = 0; gpgsm->colon.attic.line = 0; gpgsm->colon.attic.linesize = 0; gpgsm->colon.attic.linelen = 0; gpgsm->colon.any = 0; gpgsm->inline_data = NULL; gpgsm->io_cbs.add = NULL; gpgsm->io_cbs.add_priv = NULL; gpgsm->io_cbs.remove = NULL; gpgsm->io_cbs.event = NULL; gpgsm->io_cbs.event_priv = NULL; #if !USE_DESCRIPTOR_PASSING if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->input_cb.fd = fds[1]; gpgsm->input_cb.server_fd = fds[0]; if (_gpgme_io_pipe (fds, 1) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->output_cb.fd = fds[0]; gpgsm->output_cb.server_fd = fds[1]; if (_gpgme_io_pipe (fds, 0) < 0) { err = gpg_error_from_syserror (); goto leave; } gpgsm->message_cb.fd = fds[1]; gpgsm->message_cb.server_fd = fds[0]; child_fds[0] = gpgsm->input_cb.server_fd; child_fds[1] = gpgsm->output_cb.server_fd; child_fds[2] = gpgsm->message_cb.server_fd; child_fds[3] = -1; #endif argc = 0; argv[argc++] = "gpgsm"; if (home_dir) { argv[argc++] = "--homedir"; argv[argc++] = home_dir; } argv[argc++] = "--server"; argv[argc++] = NULL; err = assuan_new_ext (&gpgsm->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (gpgsm->assuan_ctx, &_gpgme_assuan_system_hooks); #if USE_DESCRIPTOR_PASSING err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (), argv, NULL, NULL, NULL, ASSUAN_PIPE_CONNECT_FDPASSING); #else { assuan_fd_t achild_fds[4]; int i; /* For now... */ for (i = 0; i < 4; i++) achild_fds[i] = (assuan_fd_t) child_fds[i]; err = assuan_pipe_connect (gpgsm->assuan_ctx, file_name ? file_name : _gpgme_get_gpgsm_path (), argv, achild_fds, NULL, NULL, 0); /* For now... */ for (i = 0; i < 4; i++) child_fds[i] = (int) achild_fds[i]; } /* On Windows, handles are inserted in the spawned process with DuplicateHandle, and child_fds contains the server-local names for the inserted handles when assuan_pipe_connect returns. */ if (!err) { /* Note: We don't use _gpgme_io_fd2str here. On W32 the returned handles are real W32 system handles, not whatever GPGME uses internally (which may be a system handle, a C library handle or a GLib/Qt channel. Confusing, yes, but remember these are server-local names, so they are not part of GPGME at all. */ snprintf (gpgsm->input_cb.server_fd_str, sizeof gpgsm->input_cb.server_fd_str, "%d", child_fds[0]); snprintf (gpgsm->output_cb.server_fd_str, sizeof gpgsm->output_cb.server_fd_str, "%d", child_fds[1]); snprintf (gpgsm->message_cb.server_fd_str, sizeof gpgsm->message_cb.server_fd_str, "%d", child_fds[2]); } #endif if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_syserror (); goto leave; } free (dft_display); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } if (isatty (1)) { int rc; rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (rc) { err = gpg_error_from_errno (rc); goto leave; } else { if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_syserror (); goto leave; } err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free (dft_ttytype); err = gpg_error_from_syserror (); goto leave; } free (dft_ttytype); err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } } } /* Ask gpgsm to enable the audit log support. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION enable-audit-log=1", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is an optional feature of gpgsm. */ } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell gpgsm to tell us when it needs it. */ if (!err) { err = assuan_transact (gpgsm->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of gpgsm. */ } #endif /*HAVE_W32_SYSTEM*/ #if !USE_DESCRIPTOR_PASSING if (!err && (_gpgme_io_set_close_notify (gpgsm->input_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->output_cb.fd, close_notify_handler, gpgsm) || _gpgme_io_set_close_notify (gpgsm->message_cb.fd, close_notify_handler, gpgsm))) { err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif leave: /* Close the server ends of the pipes (because of this, we must use the stored server_fd_str in the function start). Our ends are closed in gpgsm_release(). */ #if !USE_DESCRIPTOR_PASSING if (gpgsm->input_cb.server_fd != -1) _gpgme_io_close (gpgsm->input_cb.server_fd); if (gpgsm->output_cb.server_fd != -1) _gpgme_io_close (gpgsm->output_cb.server_fd); if (gpgsm->message_cb.server_fd != -1) _gpgme_io_close (gpgsm->message_cb.server_fd); #endif if (err) gpgsm_release (gpgsm); else *engine = gpgsm; return err; } static gpgme_error_t gpgsm_set_locale (void *engine, int category, const char *value) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *optstr; char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. GPGSM needs support for this. */ if (0) ; #ifdef LC_CTYPE else if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && gpgsm->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_ctype_set = 1; } #endif #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && gpgsm->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) gpgsm->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_syserror (); else { err = assuan_transact (gpgsm->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); } return err; } -/* Forward declaration. */ -static gpgme_status_code_t parse_status (const char *name); - static gpgme_error_t gpgsm_assuan_simple_command (assuan_context_t ctx, char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; - r = parse_status (line + 2); + r = _gpgme_parse_status (line + 2); if (r >= 0 && status_fnc) err = status_fnc (status_fnc_value, r, rest); else err = gpg_error (GPG_ERR_GENERAL); } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; static void gpgsm_clear_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type) { #if !USE_DESCRIPTOR_PASSING switch (fd_type) { case INPUT_FD: _gpgme_io_close (gpgsm->input_cb.fd); break; case OUTPUT_FD: _gpgme_io_close (gpgsm->output_cb.fd); break; case MESSAGE_FD: _gpgme_io_close (gpgsm->message_cb.fd); break; } #endif } #define COMMANDLINELEN 40 static gpgme_error_t gpgsm_set_fd (engine_gpgsm_t gpgsm, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; char *which; iocb_data_t *iocb_data; int dir; switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &gpgsm->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &gpgsm->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &gpgsm->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } dir = iocb_data->dir; #if USE_DESCRIPTOR_PASSING /* We try to short-cut the communication by giving GPGSM direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, dir) < 0) return gpg_error_from_syserror (); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, gpgsm)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (gpgsm->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); #else if (opt) snprintf (line, COMMANDLINELEN, "%s FD=%s %s", which, iocb_data->server_fd_str, opt); else snprintf (line, COMMANDLINELEN, "%s FD=%s", which, iocb_data->server_fd_str); #endif err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); #if USE_DESCRIPTOR_PASSING leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } #endif return err; } static const char * map_data_enc (gpgme_data_t d) { switch (gpgme_data_get_encoding (d)) { case GPGME_DATA_ENCODING_NONE: break; case GPGME_DATA_ENCODING_BINARY: return "--binary"; case GPGME_DATA_ENCODING_BASE64: return "--base64"; case GPGME_DATA_ENCODING_ARMOR: return "--armor"; default: break; } return NULL; } -static int -status_cmp (const void *ap, const void *bp) -{ - const struct status_table_s *a = ap; - const struct status_table_s *b = bp; - - return strcmp (a->name, b->name); -} - - -static gpgme_status_code_t -parse_status (const char *name) -{ - struct status_table_s t, *r; - t.name = name; - r = bsearch (&t, status_table, DIM(status_table) - 1, - sizeof t, status_cmp); - return r ? r->code : -1; -} - - static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_gpgsm_t gpgsm = (engine_gpgsm_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (gpgsm->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (gpgsm->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, GPGME_STATUS_EOF, ""); if (!err && gpgsm->colon.fnc && gpgsm->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ gpgsm->colon.any = 0; err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, NULL); } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (gpgsm->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &gpgsm->colon.attic.line; int *alinelen = &gpgsm->colon.attic.linelen; if (gpgsm->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_syserror (); else { *aline = newline; gpgsm->colon.attic.linesize += linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ gpgsm->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = gpgsm->colon.fnc (gpgsm->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && gpgsm->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (gpgsm->inline_data, src, linelen); if (!nwritten || (nwritten < 0 && errno != EINTR) || nwritten > linelen) { err = gpg_error_from_syserror (); break; } src += nwritten; linelen -= nwritten; } TRACE2 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; - r = parse_status (line + 2); + r = _gpgme_parse_status (line + 2); if (r >= 0) { if (gpgsm->status.fnc) err = gpgsm->status.fnc (gpgsm->status.fnc_value, r, rest); } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); TRACE3 (DEBUG_CTX, "gpgme:status_handler", gpgsm, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (gpgsm, keyword); assuan_write_line (gpgsm->assuan_ctx, "END"); } } while (!err && assuan_pending_line (gpgsm->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; TRACE_BEG2 (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_gpgsm_t gpgsm, const char *command) { gpgme_error_t err; assuan_fd_t afdlist[5]; int fdlist[5]; int nfds; int i; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (gpgsm->assuan_ctx, 0 /* read fds */, afdlist, DIM (afdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* For now... */ for (i = 0; i < nfds; i++) fdlist[i] = (int) afdlist[i]; /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ gpgsm->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (gpgsm->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (gpgsm->status_cb.fd, close_notify_handler, gpgsm)) { _gpgme_io_close (gpgsm->status_cb.fd); gpgsm->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (gpgsm, &gpgsm->status_cb, status_handler); if (!err && gpgsm->input_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->input_cb, _gpgme_data_outbound_handler); if (!err && gpgsm->output_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->output_cb, _gpgme_data_inbound_handler); if (!err && gpgsm->message_cb.fd != -1) err = add_io_cb (gpgsm, &gpgsm->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (gpgsm->assuan_ctx, command); if (!err) gpgsm_io_event (gpgsm, GPGME_EVENT_START, NULL); return err; } #if USE_DESCRIPTOR_PASSING static gpgme_error_t gpgsm_reset (void *engine) { engine_gpgsm_t gpgsm = engine; /* IF we have an active connection we must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return (gpgsm->assuan_ctx ? gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "RESET", NULL, NULL) : 0); } #endif static gpgme_error_t gpgsm_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = ciph; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm->output_cb.data = plain; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (engine, "DECRYPT"); return err; } static gpgme_error_t gpgsm_delete (void *engine, gpgme_key_t key, int allow_secret) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *fpr = key->subkeys ? key->subkeys->fpr : NULL; char *linep = fpr; char *line; int length = 8; /* "DELKEYS " */ if (!fpr) return gpg_error (GPG_ERR_INV_VALUE); while (*linep) { length++; if (*linep == '%' || *linep == ' ' || *linep == '+') length += 2; linep++; } length++; line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "DELKEYS "); linep = &line[8]; while (*fpr) { switch (*fpr) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *fpr; break; } fpr++; } *linep = '\0'; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t set_recipients (engine_gpgsm_t gpgsm, gpgme_key_t recp[]) { gpgme_error_t err = 0; assuan_context_t ctx = gpgsm->assuan_ctx; char *line; int linelen; int invalid_recipients = 0; int i; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc (10 + 40 + 1); if (!line) return gpg_error_from_syserror (); strcpy (line, "RECIPIENT "); for (i =0; !err && recp[i]; i++) { char *fpr; int newlen; if (!recp[i]->subkeys || !recp[i]->subkeys->fpr) { invalid_recipients++; continue; } fpr = recp[i]->subkeys->fpr; newlen = 11 + strlen (fpr); if (linelen < newlen) { char *newline = realloc (line, newlen); if (! newline) { int saved_errno = errno; free (line); return gpg_error_from_errno (saved_errno); } line = newline; linelen = newlen; } strcpy (&line[10], fpr); err = gpgsm_assuan_simple_command (ctx, line, gpgsm->status.fnc, gpgsm->status.fnc_value); /* FIXME: This requires more work. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if (err) { free (line); return err; } } free (line); return gpg_error (invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); } static gpgme_error_t gpgsm_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (!recp) return gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (flags & GPGME_ENCRYPT_NO_ENCRYPT_TO) { err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "OPTION no-encrypt-to", NULL, NULL); if (err) return err; } gpgsm->input_cb.data = plain; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = ciph; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = set_recipients (gpgsm, recp); if (!err) err = start (gpgsm, "ENCRYPT"); return err; } static gpgme_error_t gpgsm_export (void *engine, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *cmd; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (mode) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (!pattern) pattern = ""; cmd = malloc (7 + strlen (pattern) + 1); if (!cmd) return gpg_error_from_syserror (); strcpy (cmd, "EXPORT "); strcpy (&cmd[7], pattern); gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, cmd); free (cmd); return err; } static gpgme_error_t gpgsm_export_ext (void *engine, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata, int use_armor) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; char *line; /* Length is "EXPORT " + p + '\0'. */ int length = 7 + 1; char *linep; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (mode) return gpg_error (GPG_ERR_NOT_SUPPORTED); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); strcpy (line, "EXPORT "); linep = &line[7]; if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } pattern++; if (*pattern) *linep++ = ' '; } } *linep = '\0'; gpgsm->output_cb.data = keydata; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_genkey (void *engine, gpgme_data_t help_data, int use_armor, gpgme_data_t pubkey, gpgme_data_t seckey) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm || !pubkey || seckey) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = help_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = pubkey; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, "GENKEY"); return err; } static gpgme_error_t gpgsm_import (void *engine, gpgme_data_t keydata, gpgme_key_t *keyarray) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; gpgme_data_encoding_t dataenc; int idx; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); if (keydata && keyarray) return gpg_error (GPG_ERR_INV_VALUE); /* Only one is allowed. */ dataenc = gpgme_data_get_encoding (keydata); if (keyarray) { size_t buflen; char *buffer, *p; /* Fist check whether the engine already features the --re-import option. */ err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO cmd_has_option IMPORT re-import", NULL, NULL); if (err) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Create an internal data object with a list of all fingerprints. The data object and its memory (to avoid an extra copy by gpgme_data_new_from_mem) are stored in two variables which are released by the close_notify_handler. */ for (idx=0, buflen=0; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) buflen += strlen (keyarray[idx]->subkeys->fpr) + 1; } /* Allocate a bufer with extra space for the trailing Nul introduced by the use of stpcpy. */ buffer = malloc (buflen+1); if (!buffer) return gpg_error_from_syserror (); for (idx=0, p = buffer; keyarray[idx]; idx++) { if (keyarray[idx]->protocol == GPGME_PROTOCOL_CMS && keyarray[idx]->subkeys && keyarray[idx]->subkeys->fpr && *keyarray[idx]->subkeys->fpr) p = stpcpy (stpcpy (p, keyarray[idx]->subkeys->fpr), "\n"); } err = gpgme_data_new_from_mem (&gpgsm->input_helper_data, buffer, buflen, 0); if (err) { free (buffer); return err; } gpgsm->input_helper_memory = buffer; gpgsm->input_cb.data = gpgsm->input_helper_data; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) { gpgme_data_release (gpgsm->input_helper_data); gpgsm->input_helper_data = NULL; free (gpgsm->input_helper_memory); gpgsm->input_helper_memory = NULL; return err; } gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT --re-import"); } else if (dataenc == GPGME_DATA_ENCODING_URL || dataenc == GPGME_DATA_ENCODING_URL0 || dataenc == GPGME_DATA_ENCODING_URLESC) { return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } else { gpgsm->input_cb.data = keydata; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; return start (gpgsm, "IMPORT"); } } static gpgme_error_t gpgsm_keylist (void *engine, const char *pattern, int secret_only, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; int list_mode = 0; if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; if (!pattern) pattern = ""; /* Hack to make sure that the agent is started. Only if the agent has been started an application may connect to the agent via GPGME_PROTOCOL_ASSUAN - for example to look for smartcards. We do this only if a secret key listing has been requested. In general this is not needed because a secret key listing starts the agent. However on a fresh installation no public keys are available and thus there is no need for gpgsm to ask the agent whether a secret key exists for the public key. */ if (secret_only) gpgsm_assuan_simple_command (gpgsm->assuan_ctx, "GETINFO agent-check", NULL, NULL); /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); /* Include the ephemeral keys if requested. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_EPHEMERAL)? "OPTION with-ephemeral-keys=1": "OPTION with-ephemeral-keys=0" , NULL, NULL); /* Length is "LISTSECRETKEYS " + p + '\0'. */ line = malloc (15 + strlen (pattern) + 1); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); strcpy (&line[15], pattern); } else { strcpy (line, "LISTKEYS "); strcpy (&line[9], pattern); } gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_keylist_ext (void *engine, const char *pattern[], int secret_only, int reserved, gpgme_keylist_mode_t mode) { engine_gpgsm_t gpgsm = engine; char *line; gpgme_error_t err; /* Length is "LISTSECRETKEYS " + p + '\0'. */ int length = 15 + 1; char *linep; int any_pattern = 0; int list_mode = 0; if (reserved) return gpg_error (GPG_ERR_INV_VALUE); if (mode & GPGME_KEYLIST_MODE_LOCAL) list_mode |= 1; if (mode & GPGME_KEYLIST_MODE_EXTERN) list_mode |= 2; /* Always send list-mode option because RESET does not reset it. */ if (asprintf (&line, "OPTION list-mode=%d", (list_mode & 3)) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, line, NULL, NULL); free (line); if (err) return err; /* Always send key validation because RESET does not reset it. */ /* Use the validation mode if required. We don't check for an error yet because this is a pretty fresh gpgsm features. */ gpgsm_assuan_simple_command (gpgsm->assuan_ctx, (mode & GPGME_KEYLIST_MODE_VALIDATE)? "OPTION with-validation=1": "OPTION with-validation=0" , NULL, NULL); if (pattern && *pattern) { const char **pat = pattern; while (*pat) { const char *patlet = *pat; while (*patlet) { length++; if (*patlet == '%' || *patlet == ' ' || *patlet == '+') length += 2; patlet++; } pat++; length++; } } line = malloc (length); if (!line) return gpg_error_from_syserror (); if (secret_only) { strcpy (line, "LISTSECRETKEYS "); linep = &line[15]; } else { strcpy (line, "LISTKEYS "); linep = &line[9]; } if (pattern && *pattern) { while (*pattern) { const char *patlet = *pattern; while (*patlet) { switch (*patlet) { case '%': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '5'; break; case ' ': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = '0'; break; case '+': *(linep++) = '%'; *(linep++) = '2'; *(linep++) = 'B'; break; default: *(linep++) = *patlet; break; } patlet++; } any_pattern = 1; *linep++ = ' '; pattern++; } } if (any_pattern) linep--; *linep = '\0'; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } static gpgme_error_t gpgsm_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *assuan_cmd; int i; gpgme_key_t key; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: This does not work as RESET does not reset it so we can't revert back to default. */ if (include_certs != GPGME_INCLUDE_CERTS_DEFAULT) { /* FIXME: Make sure that if we run multiple operations, that we can reset any previously set value in case the default is requested. */ if (asprintf (&assuan_cmd, "OPTION include-certs %i", include_certs) < 0) return gpg_error_from_syserror (); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, assuan_cmd, NULL, NULL); free (assuan_cmd); if (err) return err; } for (i = 0; (key = gpgme_signers_enum (ctx, i)); i++) { const char *s = key->subkeys ? key->subkeys->fpr : NULL; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SIGNER "), s); err = gpgsm_assuan_simple_command (gpgsm->assuan_ctx, buf, gpgsm->status.fnc, gpgsm->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) return err; } gpgsm->input_cb.data = in; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; gpgsm->output_cb.data = out; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (gpgsm->output_cb.data)); if (err) return err; gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, mode == GPGME_SIG_MODE_DETACH ? "SIGN --detached" : "SIGN"); return err; } static gpgme_error_t gpgsm_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; if (!gpgsm) return gpg_error (GPG_ERR_INV_VALUE); gpgsm->input_cb.data = sig; err = gpgsm_set_fd (gpgsm, INPUT_FD, map_data_enc (gpgsm->input_cb.data)); if (err) return err; if (plaintext) { /* Normal or cleartext signature. */ gpgsm->output_cb.data = plaintext; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); gpgsm_clear_fd (gpgsm, MESSAGE_FD); } else { /* Detached signature. */ gpgsm->message_cb.data = signed_text; err = gpgsm_set_fd (gpgsm, MESSAGE_FD, 0); gpgsm_clear_fd (gpgsm, OUTPUT_FD); } gpgsm->inline_data = NULL; if (!err) err = start (gpgsm, "VERIFY"); return err; } /* Send the GETAUDITLOG command. The result is saved to a gpgme data object. */ static gpgme_error_t gpgsm_getauditlog (void *engine, gpgme_data_t output, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err = 0; if (!gpgsm || !output) return gpg_error (GPG_ERR_INV_VALUE); #if USE_DESCRIPTOR_PASSING gpgsm->output_cb.data = output; err = gpgsm_set_fd (gpgsm, OUTPUT_FD, 0); if (err) return err; gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; # define CMD "GETAUDITLOG" #else gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = output; # define CMD "GETAUDITLOG --data" #endif err = start (gpgsm, (flags & GPGME_AUDITLOG_HTML)? CMD " --html" : CMD); return err; } static void gpgsm_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->status.fnc = fnc; gpgsm->status.fnc_value = fnc_value; } static gpgme_error_t gpgsm_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_gpgsm_t gpgsm = engine; gpgsm->colon.fnc = fnc; gpgsm->colon.fnc_value = fnc_value; gpgsm->colon.any = 0; return 0; } static void gpgsm_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_gpgsm_t gpgsm = engine; gpgsm->io_cbs = *io_cbs; } static void gpgsm_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_gpgsm_t gpgsm = engine; TRACE3 (DEBUG_ENGINE, "gpgme:gpgsm_io_event", gpgsm, "event %p, type %d, type_data %p", gpgsm->io_cbs.event, type, type_data); if (gpgsm->io_cbs.event) (*gpgsm->io_cbs.event) (gpgsm->io_cbs.event_priv, type, type_data); } static gpgme_error_t gpgsm_passwd (void *engine, gpgme_key_t key, unsigned int flags) { engine_gpgsm_t gpgsm = engine; gpgme_error_t err; char *line; if (!key || !key->subkeys || !key->subkeys->fpr) return gpg_error (GPG_ERR_INV_CERT_OBJ); if (asprintf (&line, "PASSWD -- %s", key->subkeys->fpr) < 0) return gpg_error_from_syserror (); gpgsm_clear_fd (gpgsm, OUTPUT_FD); gpgsm_clear_fd (gpgsm, INPUT_FD); gpgsm_clear_fd (gpgsm, MESSAGE_FD); gpgsm->inline_data = NULL; err = start (gpgsm, line); free (line); return err; } struct engine_ops _gpgme_engine_ops_gpgsm = { /* Static functions. */ _gpgme_get_gpgsm_path, NULL, gpgsm_get_version, gpgsm_get_req_version, gpgsm_new, /* Member functions. */ gpgsm_release, #if USE_DESCRIPTOR_PASSING gpgsm_reset, #else NULL, /* reset */ #endif gpgsm_set_status_handler, NULL, /* set_command_handler */ gpgsm_set_colon_line_handler, gpgsm_set_locale, NULL, /* set_protocol */ gpgsm_decrypt, gpgsm_decrypt, gpgsm_delete, /* decrypt_verify */ NULL, /* edit */ gpgsm_encrypt, NULL, /* encrypt_sign */ gpgsm_export, gpgsm_export_ext, gpgsm_genkey, gpgsm_import, gpgsm_keylist, gpgsm_keylist_ext, gpgsm_sign, NULL, /* trustlist */ gpgsm_verify, gpgsm_getauditlog, NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ gpgsm_set_io_cbs, gpgsm_io_event, gpgsm_cancel, NULL, /* cancel_op */ gpgsm_passwd }; diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c index 92f75363..c705a4b6 100644 --- a/src/engine-uiserver.c +++ b/src/engine-uiserver.c @@ -1,1367 +1,1343 @@ /* engine-uiserver.c - Uiserver engine. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 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. */ /* Peculiar: Use special keys from email address for recipient and signer (==sender). Use no data objects with encryption for prep_encrypt. */ #if HAVE_CONFIG_H #include #endif #include #include #ifdef HAVE_SYS_TYPES_H # include #endif #include #ifdef HAVE_UNISTD_H # include #endif #include #include /* FIXME */ #include #include "gpgme.h" #include "util.h" #include "ops.h" #include "wait.h" #include "priv-io.h" #include "sema.h" #include "data.h" #include "assuan.h" #include "status-table.h" #include "debug.h" #include "engine-backend.h" typedef struct { int fd; /* FD we talk about. */ int server_fd;/* Server FD for this connection. */ int dir; /* Inbound/Outbound, maybe given implicit? */ void *data; /* Handler-specific data. */ void *tag; /* ID from the user for gpgme_remove_io_callback. */ char server_fd_str[15]; /* Same as SERVER_FD but as a string. We need this because _gpgme_io_fd2str can't be used on a closed descriptor. */ } iocb_data_t; struct engine_uiserver { assuan_context_t assuan_ctx; int lc_ctype_set; int lc_messages_set; gpgme_protocol_t protocol; iocb_data_t status_cb; /* Input, output etc are from the servers perspective. */ iocb_data_t input_cb; gpgme_data_t input_helper_data; /* Input helper data object. */ void *input_helper_memory; /* Input helper memory block. */ iocb_data_t output_cb; iocb_data_t message_cb; struct { engine_status_handler_t fnc; void *fnc_value; } status; struct { engine_colon_line_handler_t fnc; void *fnc_value; struct { char *line; int linesize; int linelen; } attic; int any; /* any data line seen */ } colon; gpgme_data_t inline_data; /* Used to collect D lines. */ struct gpgme_io_cbs io_cbs; }; typedef struct engine_uiserver *engine_uiserver_t; static void uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data); static char * uiserver_get_version (const char *file_name) { return strdup ("1.0"); } static const char * uiserver_get_req_version (void) { return "1.0"; } static void close_notify_handler (int fd, void *opaque) { engine_uiserver_t uiserver = opaque; assert (fd != -1); if (uiserver->status_cb.fd == fd) { if (uiserver->status_cb.tag) (*uiserver->io_cbs.remove) (uiserver->status_cb.tag); uiserver->status_cb.fd = -1; uiserver->status_cb.tag = NULL; } else if (uiserver->input_cb.fd == fd) { if (uiserver->input_cb.tag) (*uiserver->io_cbs.remove) (uiserver->input_cb.tag); uiserver->input_cb.fd = -1; uiserver->input_cb.tag = NULL; if (uiserver->input_helper_data) { gpgme_data_release (uiserver->input_helper_data); uiserver->input_helper_data = NULL; } if (uiserver->input_helper_memory) { free (uiserver->input_helper_memory); uiserver->input_helper_memory = NULL; } } else if (uiserver->output_cb.fd == fd) { if (uiserver->output_cb.tag) (*uiserver->io_cbs.remove) (uiserver->output_cb.tag); uiserver->output_cb.fd = -1; uiserver->output_cb.tag = NULL; } else if (uiserver->message_cb.fd == fd) { if (uiserver->message_cb.tag) (*uiserver->io_cbs.remove) (uiserver->message_cb.tag); uiserver->message_cb.fd = -1; uiserver->message_cb.tag = NULL; } } /* This is the default inquiry callback. We use it to handle the Pinentry notifications. */ static gpgme_error_t default_inq_cb (engine_uiserver_t uiserver, const char *line) { if (!strncmp (line, "PINENTRY_LAUNCHED", 17) && (line[17]==' '||!line[17])) { _gpgme_allow_set_foreground_window ((pid_t)strtoul (line+17, NULL, 10)); } return 0; } static gpgme_error_t uiserver_cancel (void *engine) { engine_uiserver_t uiserver = engine; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->status_cb.fd != -1) _gpgme_io_close (uiserver->status_cb.fd); if (uiserver->input_cb.fd != -1) _gpgme_io_close (uiserver->input_cb.fd); if (uiserver->output_cb.fd != -1) _gpgme_io_close (uiserver->output_cb.fd); if (uiserver->message_cb.fd != -1) _gpgme_io_close (uiserver->message_cb.fd); if (uiserver->assuan_ctx) { assuan_release (uiserver->assuan_ctx); uiserver->assuan_ctx = NULL; } return 0; } static void uiserver_release (void *engine) { engine_uiserver_t uiserver = engine; if (!uiserver) return; uiserver_cancel (engine); free (uiserver->colon.attic.line); free (uiserver); } static gpgme_error_t uiserver_new (void **engine, const char *file_name, const char *home_dir) { gpgme_error_t err = 0; engine_uiserver_t uiserver; char *dft_display = NULL; char dft_ttyname[64]; char *dft_ttytype = NULL; char *optstr; uiserver = calloc (1, sizeof *uiserver); if (!uiserver) return gpg_error_from_syserror (); uiserver->protocol = GPGME_PROTOCOL_DEFAULT; uiserver->status_cb.fd = -1; uiserver->status_cb.dir = 1; uiserver->status_cb.tag = 0; uiserver->status_cb.data = uiserver; uiserver->input_cb.fd = -1; uiserver->input_cb.dir = 0; uiserver->input_cb.tag = 0; uiserver->input_cb.server_fd = -1; *uiserver->input_cb.server_fd_str = 0; uiserver->output_cb.fd = -1; uiserver->output_cb.dir = 1; uiserver->output_cb.tag = 0; uiserver->output_cb.server_fd = -1; *uiserver->output_cb.server_fd_str = 0; uiserver->message_cb.fd = -1; uiserver->message_cb.dir = 0; uiserver->message_cb.tag = 0; uiserver->message_cb.server_fd = -1; *uiserver->message_cb.server_fd_str = 0; uiserver->status.fnc = 0; uiserver->colon.fnc = 0; uiserver->colon.attic.line = 0; uiserver->colon.attic.linesize = 0; uiserver->colon.attic.linelen = 0; uiserver->colon.any = 0; uiserver->inline_data = NULL; uiserver->io_cbs.add = NULL; uiserver->io_cbs.add_priv = NULL; uiserver->io_cbs.remove = NULL; uiserver->io_cbs.event = NULL; uiserver->io_cbs.event_priv = NULL; err = assuan_new_ext (&uiserver->assuan_ctx, GPG_ERR_SOURCE_GPGME, &_gpgme_assuan_malloc_hooks, _gpgme_assuan_log_cb, NULL); if (err) goto leave; assuan_ctx_set_system_hooks (uiserver->assuan_ctx, &_gpgme_assuan_system_hooks); err = assuan_socket_connect (uiserver->assuan_ctx, file_name ? file_name : _gpgme_get_uiserver_socket_path (), 0, ASSUAN_SOCKET_SERVER_FDPASSING); if (err) goto leave; err = _gpgme_getenv ("DISPLAY", &dft_display); if (err) goto leave; if (dft_display) { if (asprintf (&optstr, "OPTION display=%s", dft_display) < 0) { free (dft_display); err = gpg_error_from_errno (errno); goto leave; } free (dft_display); err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } if (isatty (1)) { int rc; rc = ttyname_r (1, dft_ttyname, sizeof (dft_ttyname)); if (rc) { err = gpg_error_from_errno (rc); goto leave; } else { if (asprintf (&optstr, "OPTION ttyname=%s", dft_ttyname) < 0) { err = gpg_error_from_errno (errno); goto leave; } err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; err = _gpgme_getenv ("TERM", &dft_ttytype); if (err) goto leave; if (dft_ttytype) { if (asprintf (&optstr, "OPTION ttytype=%s", dft_ttytype) < 0) { free (dft_ttytype); err = gpg_error_from_errno (errno); goto leave; } free (dft_ttytype); err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); if (err) goto leave; } } } #ifdef HAVE_W32_SYSTEM /* Under Windows we need to use AllowSetForegroundWindow. Tell uiserver to tell us when it needs it. */ if (!err) { err = assuan_transact (uiserver->assuan_ctx, "OPTION allow-pinentry-notify", NULL, NULL, NULL, NULL, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_UNKNOWN_OPTION) err = 0; /* This is a new feature of uiserver. */ } #endif /*HAVE_W32_SYSTEM*/ leave: if (err) uiserver_release (uiserver); else *engine = uiserver; return err; } static gpgme_error_t uiserver_set_locale (void *engine, int category, const char *value) { engine_uiserver_t uiserver = engine; gpgme_error_t err; char *optstr; char *catstr; /* FIXME: If value is NULL, we need to reset the option to default. But we can't do this. So we error out here. UISERVER needs support for this. */ if (category == LC_CTYPE) { catstr = "lc-ctype"; if (!value && uiserver->lc_ctype_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) uiserver->lc_ctype_set = 1; } #ifdef LC_MESSAGES else if (category == LC_MESSAGES) { catstr = "lc-messages"; if (!value && uiserver->lc_messages_set) return gpg_error (GPG_ERR_INV_VALUE); if (value) uiserver->lc_messages_set = 1; } #endif /* LC_MESSAGES */ else return gpg_error (GPG_ERR_INV_VALUE); /* FIXME: Reset value to default. */ if (!value) return 0; if (asprintf (&optstr, "OPTION %s=%s", catstr, value) < 0) err = gpg_error_from_errno (errno); else { err = assuan_transact (uiserver->assuan_ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL); free (optstr); } return err; } static gpgme_error_t uiserver_set_protocol (void *engine, gpgme_protocol_t protocol) { engine_uiserver_t uiserver = engine; if (protocol != GPGME_PROTOCOL_OpenPGP && protocol != GPGME_PROTOCOL_CMS && protocol != GPGME_PROTOCOL_DEFAULT) return gpg_error (GPG_ERR_INV_VALUE); uiserver->protocol = protocol; return 0; } -/* Forward declaration. */ -static gpgme_status_code_t parse_status (const char *name); - static gpgme_error_t uiserver_assuan_simple_command (assuan_context_t ctx, char *cmd, engine_status_handler_t status_fnc, void *status_fnc_value) { gpg_error_t err; char *line; size_t linelen; err = assuan_write_line (ctx, cmd); if (err) return err; do { err = assuan_read_line (ctx, &line, &linelen); if (err) return err; if (*line == '#' || !linelen) continue; if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) return 0; else if (linelen >= 4 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && line[3] == ' ') err = atoi (&line[4]); else if (linelen >= 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; - r = parse_status (line + 2); + r = _gpgme_parse_status (line + 2); if (r >= 0 && status_fnc) err = status_fnc (status_fnc_value, r, rest); else err = gpg_error (GPG_ERR_GENERAL); } else err = gpg_error (GPG_ERR_GENERAL); } while (!err); return err; } typedef enum { INPUT_FD, OUTPUT_FD, MESSAGE_FD } fd_type_t; #define COMMANDLINELEN 40 static gpgme_error_t uiserver_set_fd (engine_uiserver_t uiserver, fd_type_t fd_type, const char *opt) { gpg_error_t err = 0; char line[COMMANDLINELEN]; char *which; iocb_data_t *iocb_data; int dir; switch (fd_type) { case INPUT_FD: which = "INPUT"; iocb_data = &uiserver->input_cb; break; case OUTPUT_FD: which = "OUTPUT"; iocb_data = &uiserver->output_cb; break; case MESSAGE_FD: which = "MESSAGE"; iocb_data = &uiserver->message_cb; break; default: return gpg_error (GPG_ERR_INV_VALUE); } dir = iocb_data->dir; /* We try to short-cut the communication by giving UISERVER direct access to the file descriptor, rather than using a pipe. */ iocb_data->server_fd = _gpgme_data_get_fd (iocb_data->data); if (iocb_data->server_fd < 0) { int fds[2]; if (_gpgme_io_pipe (fds, 0) < 0) return gpg_error_from_errno (errno); iocb_data->fd = dir ? fds[0] : fds[1]; iocb_data->server_fd = dir ? fds[1] : fds[0]; if (_gpgme_io_set_close_notify (iocb_data->fd, close_notify_handler, uiserver)) { err = gpg_error (GPG_ERR_GENERAL); goto leave_set_fd; } } err = assuan_sendfd (uiserver->assuan_ctx, iocb_data->server_fd); if (err) goto leave_set_fd; _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; if (opt) snprintf (line, COMMANDLINELEN, "%s FD %s", which, opt); else snprintf (line, COMMANDLINELEN, "%s FD", which); err = uiserver_assuan_simple_command (uiserver->assuan_ctx, line, NULL, NULL); leave_set_fd: if (err) { _gpgme_io_close (iocb_data->fd); iocb_data->fd = -1; if (iocb_data->server_fd != -1) { _gpgme_io_close (iocb_data->server_fd); iocb_data->server_fd = -1; } } return err; } static const char * map_data_enc (gpgme_data_t d) { switch (gpgme_data_get_encoding (d)) { case GPGME_DATA_ENCODING_NONE: break; case GPGME_DATA_ENCODING_BINARY: return "--binary"; case GPGME_DATA_ENCODING_BASE64: return "--base64"; case GPGME_DATA_ENCODING_ARMOR: return "--armor"; default: break; } return NULL; } -static int -status_cmp (const void *ap, const void *bp) -{ - const struct status_table_s *a = ap; - const struct status_table_s *b = bp; - - return strcmp (a->name, b->name); -} - - -static gpgme_status_code_t -parse_status (const char *name) -{ - struct status_table_s t, *r; - t.name = name; - r = bsearch (&t, status_table, DIM(status_table) - 1, - sizeof t, status_cmp); - return r ? r->code : -1; -} - - static gpgme_error_t status_handler (void *opaque, int fd) { struct io_cb_data *data = (struct io_cb_data *) opaque; engine_uiserver_t uiserver = (engine_uiserver_t) data->handler_value; gpgme_error_t err = 0; char *line; size_t linelen; do { err = assuan_read_line (uiserver->assuan_ctx, &line, &linelen); if (err) { /* Try our best to terminate the connection friendly. */ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */ TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: error from assuan (%d) getting status line : %s", fd, err, gpg_strerror (err)); } else if (linelen >= 3 && line[0] == 'E' && line[1] == 'R' && line[2] == 'R' && (line[3] == '\0' || line[3] == ' ')) { if (line[3] == ' ') err = atoi (&line[4]); if (! err) err = gpg_error (GPG_ERR_GENERAL); TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: ERR line - mapped to: %s", fd, err ? gpg_strerror (err) : "ok"); /* Try our best to terminate the connection friendly. */ /* assuan_write_line (uiserver->assuan_ctx, "BYE"); */ } else if (linelen >= 2 && line[0] == 'O' && line[1] == 'K' && (line[2] == '\0' || line[2] == ' ')) { if (uiserver->status.fnc) err = uiserver->status.fnc (uiserver->status.fnc_value, GPGME_STATUS_EOF, ""); if (!err && uiserver->colon.fnc && uiserver->colon.any) { /* We must tell a colon function about the EOF. We do this only when we have seen any data lines. Note that this inlined use of colon data lines will eventually be changed into using a regular data channel. */ uiserver->colon.any = 0; err = uiserver->colon.fnc (uiserver->colon.fnc_value, NULL); } TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: OK line - final status: %s", fd, err ? gpg_strerror (err) : "ok"); _gpgme_io_close (uiserver->status_cb.fd); return err; } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && uiserver->colon.fnc) { /* We are using the colon handler even for plain inline data - strange name for that function but for historic reasons we keep it. */ /* FIXME We can't use this for binary data because we assume this is a string. For the current usage of colon output it is correct. */ char *src = line + 2; char *end = line + linelen; char *dst; char **aline = &uiserver->colon.attic.line; int *alinelen = &uiserver->colon.attic.linelen; if (uiserver->colon.attic.linesize < *alinelen + linelen + 1) { char *newline = realloc (*aline, *alinelen + linelen + 1); if (!newline) err = gpg_error_from_errno (errno); else { *aline = newline; uiserver->colon.attic.linesize += linelen + 1; } } if (!err) { dst = *aline + *alinelen; while (!err && src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst = _gpgme_hextobyte (src); (*alinelen)++; src += 2; } else { *dst = *src++; (*alinelen)++; } if (*dst == '\n') { /* Terminate the pending line, pass it to the colon handler and reset it. */ uiserver->colon.any = 1; if (*alinelen > 1 && *(dst - 1) == '\r') dst--; *dst = '\0'; /* FIXME How should we handle the return code? */ err = uiserver->colon.fnc (uiserver->colon.fnc_value, *aline); if (!err) { dst = *aline; *alinelen = 0; } } else dst++; } } TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: D line; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'D' && line[1] == ' ' && uiserver->inline_data) { char *src = line + 2; char *end = line + linelen; char *dst = src; ssize_t nwritten; linelen = 0; while (src < end) { if (*src == '%' && src + 2 < end) { /* Handle escaped characters. */ ++src; *dst++ = _gpgme_hextobyte (src); src += 2; } else *dst++ = *src++; linelen++; } src = line + 2; while (linelen > 0) { nwritten = gpgme_data_write (uiserver->inline_data, src, linelen); if (!nwritten || (nwritten < 0 && errno != EINTR) || nwritten > linelen) { err = gpg_error_from_errno (errno); break; } src += nwritten; linelen -= nwritten; } TRACE2 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: D inlinedata; final status: %s", fd, err? gpg_strerror (err):"ok"); } else if (linelen > 2 && line[0] == 'S' && line[1] == ' ') { char *rest; gpgme_status_code_t r; rest = strchr (line + 2, ' '); if (!rest) rest = line + linelen; /* set to an empty string */ else *(rest++) = 0; - r = parse_status (line + 2); + r = _gpgme_parse_status (line + 2); if (r >= 0) { if (uiserver->status.fnc) err = uiserver->status.fnc (uiserver->status.fnc_value, r, rest); } else fprintf (stderr, "[UNKNOWN STATUS]%s %s", line + 2, rest); TRACE3 (DEBUG_CTX, "gpgme:status_handler", uiserver, "fd 0x%x: S line (%s) - final status: %s", fd, line+2, err? gpg_strerror (err):"ok"); } else if (linelen >= 7 && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q' && line[3] == 'U' && line[4] == 'I' && line[5] == 'R' && line[6] == 'E' && (line[7] == '\0' || line[7] == ' ')) { char *keyword = line+7; while (*keyword == ' ') keyword++;; default_inq_cb (uiserver, keyword); assuan_write_line (uiserver->assuan_ctx, "END"); } } while (!err && assuan_pending_line (uiserver->assuan_ctx)); return err; } static gpgme_error_t add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler) { gpgme_error_t err; TRACE_BEG2 (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver, "fd %d, dir %d", iocbd->fd, iocbd->dir); err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv, iocbd->fd, iocbd->dir, handler, iocbd->data, &iocbd->tag); if (err) return TRACE_ERR (err); if (!iocbd->dir) /* FIXME Kludge around poll() problem. */ err = _gpgme_io_set_nonblocking (iocbd->fd); return TRACE_ERR (err); } static gpgme_error_t start (engine_uiserver_t uiserver, const char *command) { gpgme_error_t err; int fdlist[5]; int nfds; /* We need to know the fd used by assuan for reads. We do this by using the assumption that the first returned fd from assuan_get_active_fds() is always this one. */ nfds = assuan_get_active_fds (uiserver->assuan_ctx, 0 /* read fds */, fdlist, DIM (fdlist)); if (nfds < 1) return gpg_error (GPG_ERR_GENERAL); /* FIXME */ /* We "duplicate" the file descriptor, so we can close it here (we can't close fdlist[0], as that is closed by libassuan, and closing it here might cause libassuan to close some unrelated FD later). Alternatively, we could special case status_fd and register/unregister it manually as needed, but this increases code duplication and is more complicated as we can not use the close notifications etc. A third alternative would be to let Assuan know that we closed the FD, but that complicates the Assuan interface. */ uiserver->status_cb.fd = _gpgme_io_dup (fdlist[0]); if (uiserver->status_cb.fd < 0) return gpg_error_from_syserror (); if (_gpgme_io_set_close_notify (uiserver->status_cb.fd, close_notify_handler, uiserver)) { _gpgme_io_close (uiserver->status_cb.fd); uiserver->status_cb.fd = -1; return gpg_error (GPG_ERR_GENERAL); } err = add_io_cb (uiserver, &uiserver->status_cb, status_handler); if (!err && uiserver->input_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->input_cb, _gpgme_data_outbound_handler); if (!err && uiserver->output_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->output_cb, _gpgme_data_inbound_handler); if (!err && uiserver->message_cb.fd != -1) err = add_io_cb (uiserver, &uiserver->message_cb, _gpgme_data_outbound_handler); if (!err) err = assuan_write_line (uiserver->assuan_ctx, command); if (!err) uiserver_io_event (uiserver, GPGME_EVENT_START, NULL); return err; } static gpgme_error_t uiserver_reset (void *engine) { engine_uiserver_t uiserver = engine; /* We must send a reset because we need to reset the list of signers. Note that RESET does not reset OPTION commands. */ return uiserver_assuan_simple_command (uiserver->assuan_ctx, "RESET", NULL, NULL); } static gpgme_error_t _uiserver_decrypt (void *engine, int verify, gpgme_data_t ciph, gpgme_data_t plain) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (asprintf (&cmd, "DECRYPT%s%s", protocol, verify ? "" : " --no-verify") < 0) return gpg_error_from_errno (errno); uiserver->input_cb.data = ciph; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { free (cmd); return gpg_error (GPG_ERR_GENERAL); /* FIXME */ } uiserver->output_cb.data = plain; err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); if (err) { free (cmd); return gpg_error (GPG_ERR_GENERAL); /* FIXME */ } uiserver->inline_data = NULL; err = start (engine, cmd); free (cmd); return err; } static gpgme_error_t uiserver_decrypt (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { return _uiserver_decrypt (engine, 0, ciph, plain); } static gpgme_error_t uiserver_decrypt_verify (void *engine, gpgme_data_t ciph, gpgme_data_t plain) { return _uiserver_decrypt (engine, 1, ciph, plain); } static gpgme_error_t set_recipients (engine_uiserver_t uiserver, gpgme_key_t recp[]) { gpgme_error_t err = 0; assuan_context_t ctx = uiserver->assuan_ctx; char *line; int linelen; int invalid_recipients = 0; int i; linelen = 10 + 40 + 1; /* "RECIPIENT " + guess + '\0'. */ line = malloc (10 + 40 + 1); if (!line) return gpg_error_from_errno (errno); strcpy (line, "RECIPIENT "); for (i=0; !err && recp[i]; i++) { char *uid; int newlen; /* We use only the first user ID of the key. */ if (!recp[i]->uids || !(uid=recp[i]->uids->uid) || !*uid) { invalid_recipients++; continue; } newlen = 11 + strlen (uid); if (linelen < newlen) { char *newline = realloc (line, newlen); if (! newline) { int saved_errno = errno; free (line); return gpg_error_from_errno (saved_errno); } line = newline; linelen = newlen; } /* FIXME: need to do proper escaping */ strcpy (&line[10], uid); err = uiserver_assuan_simple_command (ctx, line, uiserver->status.fnc, uiserver->status.fnc_value); /* FIXME: This might requires more work. */ if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) invalid_recipients++; else if (err) { free (line); return err; } } free (line); return gpg_error (invalid_recipients ? GPG_ERR_UNUSABLE_PUBKEY : GPG_ERR_NO_ERROR); } static gpgme_error_t uiserver_encrypt (void *engine, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t ciph, int use_armor) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (flags & GPGME_ENCRYPT_PREPARE) { if (!recp || plain || ciph) return gpg_error (GPG_ERR_INV_VALUE); if (asprintf (&cmd, "PREP_ENCRYPT%s%s", protocol, (flags & GPGME_ENCRYPT_EXPECT_SIGN) ? " --expect-sign" : "") < 0) return gpg_error_from_errno (errno); } else { if (!plain || !ciph) return gpg_error (GPG_ERR_INV_VALUE); if (asprintf (&cmd, "ENCRYPT%s", protocol) < 0) return gpg_error_from_errno (errno); } if (plain) { uiserver->input_cb.data = plain; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { free (cmd); return err; } } if (ciph) { uiserver->output_cb.data = ciph; err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (uiserver->output_cb.data)); if (err) { free (cmd); return err; } } uiserver->inline_data = NULL; if (recp) { err = set_recipients (uiserver, recp); if (err) { free (cmd); return err; } } err = start (uiserver, cmd); free (cmd); return err; } static gpgme_error_t uiserver_sign (void *engine, gpgme_data_t in, gpgme_data_t out, gpgme_sig_mode_t mode, int use_armor, int use_textmode, int include_certs, gpgme_ctx_t ctx /* FIXME */) { engine_uiserver_t uiserver = engine; gpgme_error_t err = 0; const char *protocol; char *cmd; gpgme_key_t key; if (!uiserver || !in || !out) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (asprintf (&cmd, "SIGN%s%s", protocol, (mode == GPGME_SIG_MODE_DETACH) ? " --detached" : "") < 0) return gpg_error_from_errno (errno); key = gpgme_signers_enum (ctx, 0); if (key) { const char *s = NULL; if (key && key->uids) s = key->uids->email; if (s && strlen (s) < 80) { char buf[100]; strcpy (stpcpy (buf, "SENDER --info "), s); err = uiserver_assuan_simple_command (uiserver->assuan_ctx, buf, uiserver->status.fnc, uiserver->status.fnc_value); } else err = gpg_error (GPG_ERR_INV_VALUE); gpgme_key_unref (key); if (err) { free (cmd); return err; } } uiserver->input_cb.data = in; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { free (cmd); return err; } uiserver->output_cb.data = out; err = uiserver_set_fd (uiserver, OUTPUT_FD, use_armor ? "--armor" : map_data_enc (uiserver->output_cb.data)); if (err) { free (cmd); return err; } uiserver->inline_data = NULL; err = start (uiserver, cmd); free (cmd); return err; } /* FIXME: Missing a way to specify --silent. */ static gpgme_error_t uiserver_verify (void *engine, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext) { engine_uiserver_t uiserver = engine; gpgme_error_t err; const char *protocol; char *cmd; if (!uiserver) return gpg_error (GPG_ERR_INV_VALUE); if (uiserver->protocol == GPGME_PROTOCOL_DEFAULT) protocol = ""; else if (uiserver->protocol == GPGME_PROTOCOL_OpenPGP) protocol = " --protocol=OpenPGP"; else if (uiserver->protocol == GPGME_PROTOCOL_CMS) protocol = " --protocol=CMS"; else return gpgme_error (GPG_ERR_UNSUPPORTED_PROTOCOL); if (asprintf (&cmd, "VERIFY%s", protocol) < 0) return gpg_error_from_errno (errno); uiserver->input_cb.data = sig; err = uiserver_set_fd (uiserver, INPUT_FD, map_data_enc (uiserver->input_cb.data)); if (err) { free (cmd); return err; } if (plaintext) { /* Normal or cleartext signature. */ uiserver->output_cb.data = plaintext; err = uiserver_set_fd (uiserver, OUTPUT_FD, 0); } else { /* Detached signature. */ uiserver->message_cb.data = signed_text; err = uiserver_set_fd (uiserver, MESSAGE_FD, 0); } uiserver->inline_data = NULL; if (!err) err = start (uiserver, cmd); free (cmd); return err; } static void uiserver_set_status_handler (void *engine, engine_status_handler_t fnc, void *fnc_value) { engine_uiserver_t uiserver = engine; uiserver->status.fnc = fnc; uiserver->status.fnc_value = fnc_value; } static gpgme_error_t uiserver_set_colon_line_handler (void *engine, engine_colon_line_handler_t fnc, void *fnc_value) { engine_uiserver_t uiserver = engine; uiserver->colon.fnc = fnc; uiserver->colon.fnc_value = fnc_value; uiserver->colon.any = 0; return 0; } static void uiserver_set_io_cbs (void *engine, gpgme_io_cbs_t io_cbs) { engine_uiserver_t uiserver = engine; uiserver->io_cbs = *io_cbs; } static void uiserver_io_event (void *engine, gpgme_event_io_t type, void *type_data) { engine_uiserver_t uiserver = engine; TRACE3 (DEBUG_ENGINE, "gpgme:uiserver_io_event", uiserver, "event %p, type %d, type_data %p", uiserver->io_cbs.event, type, type_data); if (uiserver->io_cbs.event) (*uiserver->io_cbs.event) (uiserver->io_cbs.event_priv, type, type_data); } struct engine_ops _gpgme_engine_ops_uiserver = { /* Static functions. */ _gpgme_get_uiserver_socket_path, NULL, uiserver_get_version, uiserver_get_req_version, uiserver_new, /* Member functions. */ uiserver_release, uiserver_reset, uiserver_set_status_handler, NULL, /* set_command_handler */ uiserver_set_colon_line_handler, uiserver_set_locale, uiserver_set_protocol, uiserver_decrypt, uiserver_decrypt_verify, NULL, /* delete */ NULL, /* edit */ uiserver_encrypt, NULL, /* encrypt_sign */ NULL, /* export */ NULL, /* export_ext */ NULL, /* genkey */ NULL, /* import */ NULL, /* keylist */ NULL, /* keylist_ext */ uiserver_sign, NULL, /* trustlist */ uiserver_verify, NULL, /* getauditlog */ NULL, /* opassuan_transact */ NULL, /* conf_load */ NULL, /* conf_save */ uiserver_set_io_cbs, uiserver_io_event, uiserver_cancel, NULL /* cancel_op */ }; diff --git a/src/extra-stati.h b/src/extra-stati.h deleted file mode 100644 index 59cccf98..00000000 --- a/src/extra-stati.h +++ /dev/null @@ -1,29 +0,0 @@ -/* extra-stati.lst - Extra GnuPG status codes. - Copyright 2011 g10 Code GmbH - - This file is free software; as a special exception the author gives - unlimited permission to copy and/or distribute it, with or without - modifications, as long as this notice is preserved. - - This file is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY, to the extent permitted by law; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. */ - -/* A list of internal status code to be processed by mkstatus. Those - * status codes are not part of the API but internally required by - * gpgme. We use a second enum type here but make sure that the - * values don't clash with those of gpgme_status_code_t. - */ - -enum - { - /* This value is the first used one. It needs to be larger than - the last value of gpgme_status_code_t. There is no need to - explictly list the values because they are internal only. */ - _GPGME_STATUS_FIRST_EXTRA = 192, - - GPGME_STATUS_DECRYPTION_INFO, - - _GPGME_STATUS_LAST_EXTRA - }; diff --git a/src/gpgme.h.in b/src/gpgme.h.in index e75de194..dad8d948 100644 --- a/src/gpgme.h.in +++ b/src/gpgme.h.in @@ -1,2116 +1,2116 @@ /* gpgme.h - Public interface to GnuPG Made Easy. -*- c -*- Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 2010 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, see . File: @configure_input@ */ #ifndef GPGME_H #define GPGME_H #ifdef __GNUC__ #define _GPGME_INLINE __inline__ #elif __STDC_VERSION__ >= 199901L #define _GPGME_INLINE inline #else #define _GPGME_INLINE #endif /* Include stdio.h for the FILE type definition. */ #include @INSERT__TYPEDEFS_FOR_GPGME_H@ #include #include #ifdef __cplusplus extern "C" { #if 0 /* just to make Emacs auto-indent happy */ } #endif #endif /* __cplusplus */ /* Check for compiler features. */ #if __GNUC__ #define _GPGME_GCC_VERSION (__GNUC__ * 10000 \ + __GNUC_MINOR__ * 100 \ + __GNUC_PATCHLEVEL__) #if _GPGME_GCC_VERSION > 30100 #define _GPGME_DEPRECATED __attribute__ ((__deprecated__)) #endif #endif #ifndef _GPGME_DEPRECATED #define _GPGME_DEPRECATED #endif /* The macro _GPGME_DEPRECATED_OUTSIDE_GPGME suppresses warnings for fields we must access in GPGME for ABI compatibility. */ #ifdef _GPGME_IN_GPGME #define _GPGME_DEPRECATED_OUTSIDE_GPGME #else #define _GPGME_DEPRECATED_OUTSIDE_GPGME _GPGME_DEPRECATED #endif /* The version of this header should match the one of the library. Do not use this symbol in your application, use gpgme_check_version instead. The purpose of this macro is to let autoconf (using the AM_PATH_GPGME macro) check that this header matches the installed library. */ #define GPGME_VERSION "@PACKAGE_VERSION@" /* Check for a matching _FILE_OFFSET_BITS definition. */ #if @NEED__FILE_OFFSET_BITS@ #ifndef _FILE_OFFSET_BITS #error GPGME was compiled with _FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@, please see the section "Largefile support (LFS)" in the GPGME manual. #else #if (_FILE_OFFSET_BITS) != (@NEED__FILE_OFFSET_BITS@) #error GPGME was compiled with a different value for _FILE_OFFSET_BITS, namely @NEED__FILE_OFFSET_BITS@, please see the section "Largefile support (LFS)" in the GPGME manual. #endif #endif #endif /* Some opaque data types used by GPGME. */ /* The context holds some global state and configration options, as well as the results of a crypto operation. */ struct gpgme_context; typedef struct gpgme_context *gpgme_ctx_t; /* The data object is used by GPGME to exchange arbitrary data. */ struct gpgme_data; typedef struct gpgme_data *gpgme_data_t; /* Wrappers for the libgpg-error library. */ typedef gpg_error_t gpgme_error_t; typedef gpg_err_code_t gpgme_err_code_t; typedef gpg_err_source_t gpgme_err_source_t; static _GPGME_INLINE gpgme_error_t gpgme_err_make (gpgme_err_source_t source, gpgme_err_code_t code) { return gpg_err_make (source, code); } /* The user can define GPGME_ERR_SOURCE_DEFAULT before including this file to specify a default source for gpgme_error. */ #ifndef GPGME_ERR_SOURCE_DEFAULT #define GPGME_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_USER_1 #endif static _GPGME_INLINE gpgme_error_t gpgme_error (gpgme_err_code_t code) { return gpgme_err_make (GPGME_ERR_SOURCE_DEFAULT, code); } static _GPGME_INLINE gpgme_err_code_t gpgme_err_code (gpgme_error_t err) { return gpg_err_code (err); } static _GPGME_INLINE gpgme_err_source_t gpgme_err_source (gpgme_error_t err) { return gpg_err_source (err); } /* Return a pointer to a string containing a description of the error code in the error value ERR. This function is not thread safe. */ const char *gpgme_strerror (gpgme_error_t err); /* Return the error string for ERR in the user-supplied buffer BUF of size BUFLEN. This function is, in contrast to gpg_strerror, thread-safe if a thread-safe strerror_r() function is provided by the system. If the function succeeds, 0 is returned and BUF contains the string describing the error. If the buffer was not large enough, ERANGE is returned and BUF contains as much of the beginning of the error string as fits into the buffer. */ int gpgme_strerror_r (gpg_error_t err, char *buf, size_t buflen); /* Return a pointer to a string containing a description of the error source in the error value ERR. */ const char *gpgme_strsource (gpgme_error_t err); /* Retrieve the error code for the system error ERR. This returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report this). */ gpgme_err_code_t gpgme_err_code_from_errno (int err); /* Retrieve the system error for the error code CODE. This returns 0 if CODE is not a system error code. */ int gpgme_err_code_to_errno (gpgme_err_code_t code); /* Retrieve the error code directly from the ERRNO variable. This returns GPG_ERR_UNKNOWN_ERRNO if the system error is not mapped (report this) and GPG_ERR_MISSING_ERRNO if ERRNO has the value 0. */ gpgme_err_code_t gpgme_err_code_from_syserror (void); /* Set the ERRNO variable. This function is the preferred way to set ERRNO due to peculiarities on WindowsCE. */ void gpgme_err_set_errno (int err); /* Return an error value with the error source SOURCE and the system error ERR. FIXME: Should be inline. */ gpgme_error_t gpgme_err_make_from_errno (gpgme_err_source_t source, int err); /* Return an error value with the system error ERR. FIXME: Should be inline. */ gpgme_error_t gpgme_error_from_errno (int err); static _GPGME_INLINE gpgme_error_t gpgme_error_from_syserror (void) { return gpgme_error (gpgme_err_code_from_syserror ()); } /* The possible encoding mode of gpgme_data_t objects. */ typedef enum { GPGME_DATA_ENCODING_NONE = 0, /* Not specified. */ GPGME_DATA_ENCODING_BINARY = 1, GPGME_DATA_ENCODING_BASE64 = 2, GPGME_DATA_ENCODING_ARMOR = 3, /* Either PEM or OpenPGP Armor. */ GPGME_DATA_ENCODING_URL = 4, /* LF delimited URL list. */ GPGME_DATA_ENCODING_URLESC = 5, /* Ditto, but percent escaped. */ GPGME_DATA_ENCODING_URL0 = 6 /* Nul delimited URL list. */ } gpgme_data_encoding_t; /* Public key algorithms from libgcrypt. */ typedef enum { GPGME_PK_RSA = 1, GPGME_PK_RSA_E = 2, GPGME_PK_RSA_S = 3, GPGME_PK_ELG_E = 16, GPGME_PK_DSA = 17, GPGME_PK_ELG = 20, GPGME_PK_ECDSA = 301, GPGME_PK_ECDH = 302 } gpgme_pubkey_algo_t; /* Hash algorithms from libgcrypt. */ typedef enum { GPGME_MD_NONE = 0, GPGME_MD_MD5 = 1, GPGME_MD_SHA1 = 2, GPGME_MD_RMD160 = 3, GPGME_MD_MD2 = 5, GPGME_MD_TIGER = 6, /* TIGER/192. */ GPGME_MD_HAVAL = 7, /* HAVAL, 5 pass, 160 bit. */ GPGME_MD_SHA256 = 8, GPGME_MD_SHA384 = 9, GPGME_MD_SHA512 = 10, GPGME_MD_MD4 = 301, GPGME_MD_CRC32 = 302, GPGME_MD_CRC32_RFC1510 = 303, GPGME_MD_CRC24_RFC2440 = 304 } gpgme_hash_algo_t; /* The possible signature stati. Deprecated, use error value in sig status. */ typedef enum { GPGME_SIG_STAT_NONE = 0, GPGME_SIG_STAT_GOOD = 1, GPGME_SIG_STAT_BAD = 2, GPGME_SIG_STAT_NOKEY = 3, GPGME_SIG_STAT_NOSIG = 4, GPGME_SIG_STAT_ERROR = 5, GPGME_SIG_STAT_DIFF = 6, GPGME_SIG_STAT_GOOD_EXP = 7, GPGME_SIG_STAT_GOOD_EXPKEY = 8 } _gpgme_sig_stat_t; typedef _gpgme_sig_stat_t gpgme_sig_stat_t _GPGME_DEPRECATED; /* The available signature modes. */ typedef enum { GPGME_SIG_MODE_NORMAL = 0, GPGME_SIG_MODE_DETACH = 1, GPGME_SIG_MODE_CLEAR = 2 } gpgme_sig_mode_t; /* The available key and signature attributes. Deprecated, use the individual result structures instead. */ typedef enum { GPGME_ATTR_KEYID = 1, GPGME_ATTR_FPR = 2, GPGME_ATTR_ALGO = 3, GPGME_ATTR_LEN = 4, GPGME_ATTR_CREATED = 5, GPGME_ATTR_EXPIRE = 6, GPGME_ATTR_OTRUST = 7, GPGME_ATTR_USERID = 8, GPGME_ATTR_NAME = 9, GPGME_ATTR_EMAIL = 10, GPGME_ATTR_COMMENT = 11, GPGME_ATTR_VALIDITY = 12, GPGME_ATTR_LEVEL = 13, GPGME_ATTR_TYPE = 14, GPGME_ATTR_IS_SECRET = 15, GPGME_ATTR_KEY_REVOKED = 16, GPGME_ATTR_KEY_INVALID = 17, GPGME_ATTR_UID_REVOKED = 18, GPGME_ATTR_UID_INVALID = 19, GPGME_ATTR_KEY_CAPS = 20, GPGME_ATTR_CAN_ENCRYPT = 21, GPGME_ATTR_CAN_SIGN = 22, GPGME_ATTR_CAN_CERTIFY = 23, GPGME_ATTR_KEY_EXPIRED = 24, GPGME_ATTR_KEY_DISABLED = 25, GPGME_ATTR_SERIAL = 26, GPGME_ATTR_ISSUER = 27, GPGME_ATTR_CHAINID = 28, GPGME_ATTR_SIG_STATUS = 29, GPGME_ATTR_ERRTOK = 30, GPGME_ATTR_SIG_SUMMARY = 31, GPGME_ATTR_SIG_CLASS = 32 } _gpgme_attr_t; typedef _gpgme_attr_t gpgme_attr_t _GPGME_DEPRECATED; /* The available validities for a trust item or key. */ typedef enum { GPGME_VALIDITY_UNKNOWN = 0, GPGME_VALIDITY_UNDEFINED = 1, GPGME_VALIDITY_NEVER = 2, GPGME_VALIDITY_MARGINAL = 3, GPGME_VALIDITY_FULL = 4, GPGME_VALIDITY_ULTIMATE = 5 } gpgme_validity_t; /* The available protocols. */ typedef enum { GPGME_PROTOCOL_OpenPGP = 0, /* The default mode. */ GPGME_PROTOCOL_CMS = 1, GPGME_PROTOCOL_GPGCONF = 2, /* Special code for gpgconf. */ GPGME_PROTOCOL_ASSUAN = 3, /* Low-level access to an Assuan server. */ GPGME_PROTOCOL_G13 = 4, GPGME_PROTOCOL_UISERVER= 5, GPGME_PROTOCOL_DEFAULT = 254, GPGME_PROTOCOL_UNKNOWN = 255 } gpgme_protocol_t; /* The available keylist mode flags. */ #define GPGME_KEYLIST_MODE_LOCAL 1 #define GPGME_KEYLIST_MODE_EXTERN 2 #define GPGME_KEYLIST_MODE_SIGS 4 #define GPGME_KEYLIST_MODE_SIG_NOTATIONS 8 #define GPGME_KEYLIST_MODE_EPHEMERAL 128 #define GPGME_KEYLIST_MODE_VALIDATE 256 typedef unsigned int gpgme_keylist_mode_t; /* The available export mode flags. */ #define GPGME_EXPORT_MODE_EXTERN 2 #define GPGME_EXPORT_MODE_MINIMAL 4 typedef unsigned int gpgme_export_mode_t; /* Flags for the audit log functions. */ #define GPGME_AUDITLOG_HTML 1 #define GPGME_AUDITLOG_WITH_HELP 128 /* Signature notations. */ /* The available signature notation flags. */ #define GPGME_SIG_NOTATION_HUMAN_READABLE 1 #define GPGME_SIG_NOTATION_CRITICAL 2 typedef unsigned int gpgme_sig_notation_flags_t; struct _gpgme_sig_notation { struct _gpgme_sig_notation *next; /* If NAME is a null pointer, then VALUE contains a policy URL rather than a notation. */ char *name; /* The value of the notation data. */ char *value; /* The length of the name of the notation data. */ int name_len; /* The length of the value of the notation data. */ int value_len; /* The accumulated flags. */ gpgme_sig_notation_flags_t flags; /* Notation data is human-readable. */ unsigned int human_readable : 1; /* Notation data is critical. */ unsigned int critical : 1; /* Internal to GPGME, do not use. */ int _unused : 30; }; typedef struct _gpgme_sig_notation *gpgme_sig_notation_t; /* The possible stati for the edit operation. */ typedef enum { GPGME_STATUS_EOF = 0, /* mkstatus processing starts here */ GPGME_STATUS_ENTER = 1, GPGME_STATUS_LEAVE = 2, GPGME_STATUS_ABORT = 3, GPGME_STATUS_GOODSIG = 4, GPGME_STATUS_BADSIG = 5, GPGME_STATUS_ERRSIG = 6, GPGME_STATUS_BADARMOR = 7, GPGME_STATUS_RSA_OR_IDEA = 8, GPGME_STATUS_KEYEXPIRED = 9, GPGME_STATUS_KEYREVOKED = 10, GPGME_STATUS_TRUST_UNDEFINED = 11, GPGME_STATUS_TRUST_NEVER = 12, GPGME_STATUS_TRUST_MARGINAL = 13, GPGME_STATUS_TRUST_FULLY = 14, GPGME_STATUS_TRUST_ULTIMATE = 15, GPGME_STATUS_SHM_INFO = 16, GPGME_STATUS_SHM_GET = 17, GPGME_STATUS_SHM_GET_BOOL = 18, GPGME_STATUS_SHM_GET_HIDDEN = 19, GPGME_STATUS_NEED_PASSPHRASE = 20, GPGME_STATUS_VALIDSIG = 21, GPGME_STATUS_SIG_ID = 22, GPGME_STATUS_ENC_TO = 23, GPGME_STATUS_NODATA = 24, GPGME_STATUS_BAD_PASSPHRASE = 25, GPGME_STATUS_NO_PUBKEY = 26, GPGME_STATUS_NO_SECKEY = 27, GPGME_STATUS_NEED_PASSPHRASE_SYM = 28, GPGME_STATUS_DECRYPTION_FAILED = 29, GPGME_STATUS_DECRYPTION_OKAY = 30, GPGME_STATUS_MISSING_PASSPHRASE = 31, GPGME_STATUS_GOOD_PASSPHRASE = 32, GPGME_STATUS_GOODMDC = 33, GPGME_STATUS_BADMDC = 34, GPGME_STATUS_ERRMDC = 35, GPGME_STATUS_IMPORTED = 36, GPGME_STATUS_IMPORT_OK = 37, GPGME_STATUS_IMPORT_PROBLEM = 38, GPGME_STATUS_IMPORT_RES = 39, GPGME_STATUS_FILE_START = 40, GPGME_STATUS_FILE_DONE = 41, GPGME_STATUS_FILE_ERROR = 42, GPGME_STATUS_BEGIN_DECRYPTION = 43, GPGME_STATUS_END_DECRYPTION = 44, GPGME_STATUS_BEGIN_ENCRYPTION = 45, GPGME_STATUS_END_ENCRYPTION = 46, GPGME_STATUS_DELETE_PROBLEM = 47, GPGME_STATUS_GET_BOOL = 48, GPGME_STATUS_GET_LINE = 49, GPGME_STATUS_GET_HIDDEN = 50, GPGME_STATUS_GOT_IT = 51, GPGME_STATUS_PROGRESS = 52, GPGME_STATUS_SIG_CREATED = 53, GPGME_STATUS_SESSION_KEY = 54, GPGME_STATUS_NOTATION_NAME = 55, GPGME_STATUS_NOTATION_DATA = 56, GPGME_STATUS_POLICY_URL = 57, GPGME_STATUS_BEGIN_STREAM = 58, GPGME_STATUS_END_STREAM = 59, GPGME_STATUS_KEY_CREATED = 60, GPGME_STATUS_USERID_HINT = 61, GPGME_STATUS_UNEXPECTED = 62, GPGME_STATUS_INV_RECP = 63, GPGME_STATUS_NO_RECP = 64, GPGME_STATUS_ALREADY_SIGNED = 65, GPGME_STATUS_SIGEXPIRED = 66, GPGME_STATUS_EXPSIG = 67, GPGME_STATUS_EXPKEYSIG = 68, GPGME_STATUS_TRUNCATED = 69, GPGME_STATUS_ERROR = 70, GPGME_STATUS_NEWSIG = 71, GPGME_STATUS_REVKEYSIG = 72, GPGME_STATUS_SIG_SUBPACKET = 73, GPGME_STATUS_NEED_PASSPHRASE_PIN = 74, GPGME_STATUS_SC_OP_FAILURE = 75, GPGME_STATUS_SC_OP_SUCCESS = 76, GPGME_STATUS_CARDCTRL = 77, GPGME_STATUS_BACKUP_KEY_CREATED = 78, GPGME_STATUS_PKA_TRUST_BAD = 79, GPGME_STATUS_PKA_TRUST_GOOD = 80, - GPGME_STATUS_PLAINTEXT = 81, GPGME_STATUS_INV_SGNR = 82, GPGME_STATUS_NO_SGNR = 83, - GPGME_STATUS_SUCCESS = 84 + GPGME_STATUS_SUCCESS = 84, + GPGME_STATUS_DECRYPTION_INFO = 85 } gpgme_status_code_t; /* The engine information structure. */ struct _gpgme_engine_info { struct _gpgme_engine_info *next; /* The protocol ID. */ gpgme_protocol_t protocol; /* The file name of the engine binary. */ char *file_name; /* The version string of the installed engine. */ char *version; /* The minimum version required for GPGME. */ const char *req_version; /* The home directory used, or NULL if default. */ char *home_dir; }; typedef struct _gpgme_engine_info *gpgme_engine_info_t; /* A subkey from a key. */ struct _gpgme_subkey { struct _gpgme_subkey *next; /* True if subkey is revoked. */ unsigned int revoked : 1; /* True if subkey is expired. */ unsigned int expired : 1; /* True if subkey is disabled. */ unsigned int disabled : 1; /* True if subkey is invalid. */ unsigned int invalid : 1; /* True if subkey can be used for encryption. */ unsigned int can_encrypt : 1; /* True if subkey can be used for signing. */ unsigned int can_sign : 1; /* True if subkey can be used for certification. */ unsigned int can_certify : 1; /* True if subkey is secret. */ unsigned int secret : 1; /* True if subkey can be used for authentication. */ unsigned int can_authenticate : 1; /* True if subkey is qualified for signatures according to German law. */ unsigned int is_qualified : 1; /* True if the secret key is stored on a smart card. */ unsigned int is_cardkey : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 21; /* Public key algorithm supported by this subkey. */ gpgme_pubkey_algo_t pubkey_algo; /* Length of the subkey. */ unsigned int length; /* The key ID of the subkey. */ char *keyid; /* Internal to GPGME, do not use. */ char _keyid[16 + 1]; /* The fingerprint of the subkey in hex digit form. */ char *fpr; /* The creation timestamp, -1 if invalid, 0 if not available. */ long int timestamp; /* The expiration timestamp, 0 if the subkey does not expire. */ long int expires; /* The serial number of a smart card holding this key or NULL. */ char *card_number; }; typedef struct _gpgme_subkey *gpgme_subkey_t; /* A signature on a user ID. */ struct _gpgme_key_sig { struct _gpgme_key_sig *next; /* True if the signature is a revocation signature. */ unsigned int revoked : 1; /* True if the signature is expired. */ unsigned int expired : 1; /* True if the signature is invalid. */ unsigned int invalid : 1; /* True if the signature should be exported. */ unsigned int exportable : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 28; /* The public key algorithm used to create the signature. */ gpgme_pubkey_algo_t pubkey_algo; /* The key ID of key used to create the signature. */ char *keyid; /* Internal to GPGME, do not use. */ char _keyid[16 + 1]; /* The creation timestamp, -1 if invalid, 0 if not available. */ long int timestamp; /* The expiration timestamp, 0 if the subkey does not expire. */ long int expires; /* Same as in gpgme_signature_t. */ gpgme_error_t status; #ifdef __cplusplus unsigned int _obsolete_class _GPGME_DEPRECATED; #else /* Must be set to SIG_CLASS below. */ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME; #endif /* The user ID string. */ char *uid; /* The name part of the user ID. */ char *name; /* The email part of the user ID. */ char *email; /* The comment part of the user ID. */ char *comment; /* Crypto backend specific signature class. */ unsigned int sig_class; /* Notation data and policy URLs. */ gpgme_sig_notation_t notations; /* Internal to GPGME, do not use. */ gpgme_sig_notation_t _last_notation; }; typedef struct _gpgme_key_sig *gpgme_key_sig_t; /* An user ID from a key. */ struct _gpgme_user_id { struct _gpgme_user_id *next; /* True if the user ID is revoked. */ unsigned int revoked : 1; /* True if the user ID is invalid. */ unsigned int invalid : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 30; /* The validity of the user ID. */ gpgme_validity_t validity; /* The user ID string. */ char *uid; /* The name part of the user ID. */ char *name; /* The email part of the user ID. */ char *email; /* The comment part of the user ID. */ char *comment; /* The signatures of the user ID. */ gpgme_key_sig_t signatures; /* Internal to GPGME, do not use. */ gpgme_key_sig_t _last_keysig; }; typedef struct _gpgme_user_id *gpgme_user_id_t; /* A key from the keyring. */ struct _gpgme_key { /* Internal to GPGME, do not use. */ unsigned int _refs; /* True if key is revoked. */ unsigned int revoked : 1; /* True if key is expired. */ unsigned int expired : 1; /* True if key is disabled. */ unsigned int disabled : 1; /* True if key is invalid. */ unsigned int invalid : 1; /* True if key can be used for encryption. */ unsigned int can_encrypt : 1; /* True if key can be used for signing. */ unsigned int can_sign : 1; /* True if key can be used for certification. */ unsigned int can_certify : 1; /* True if key is secret. */ unsigned int secret : 1; /* True if key can be used for authentication. */ unsigned int can_authenticate : 1; /* True if subkey is qualified for signatures according to German law. */ unsigned int is_qualified : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 22; /* This is the protocol supported by this key. */ gpgme_protocol_t protocol; /* If protocol is GPGME_PROTOCOL_CMS, this string contains the issuer serial. */ char *issuer_serial; /* If protocol is GPGME_PROTOCOL_CMS, this string contains the issuer name. */ char *issuer_name; /* If protocol is GPGME_PROTOCOL_CMS, this string contains the chain ID. */ char *chain_id; /* If protocol is GPGME_PROTOCOL_OpenPGP, this field contains the owner trust. */ gpgme_validity_t owner_trust; /* The subkeys of the key. */ gpgme_subkey_t subkeys; /* The user IDs of the key. */ gpgme_user_id_t uids; /* Internal to GPGME, do not use. */ gpgme_subkey_t _last_subkey; /* Internal to GPGME, do not use. */ gpgme_user_id_t _last_uid; /* The keylist mode that was active when listing the key. */ gpgme_keylist_mode_t keylist_mode; }; typedef struct _gpgme_key *gpgme_key_t; /* Types for callback functions. */ /* Request a passphrase from the user. */ typedef gpgme_error_t (*gpgme_passphrase_cb_t) (void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd); /* Inform the user about progress made. */ typedef void (*gpgme_progress_cb_t) (void *opaque, const char *what, int type, int current, int total); /* Interact with the user about an edit operation. */ typedef gpgme_error_t (*gpgme_edit_cb_t) (void *opaque, gpgme_status_code_t status, const char *args, int fd); /* Context management functions. */ /* Create a new context and return it in CTX. */ gpgme_error_t gpgme_new (gpgme_ctx_t *ctx); /* Release the context CTX. */ void gpgme_release (gpgme_ctx_t ctx); /* Set the protocol to be used by CTX to PROTO. */ gpgme_error_t gpgme_set_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto); /* Get the protocol used with CTX */ gpgme_protocol_t gpgme_get_protocol (gpgme_ctx_t ctx); /* Set the crypto protocol to be used by CTX to PROTO. gpgme_set_protocol actually sets the backend engine. This sets the crypto protocol used in engines that support more than one crypto prococol (for example, an UISERVER can support OpenPGP and CMS). This is reset to the default with gpgme_set_protocol. */ gpgme_error_t gpgme_set_sub_protocol (gpgme_ctx_t ctx, gpgme_protocol_t proto); /* Get the sub protocol. */ gpgme_protocol_t gpgme_get_sub_protocol (gpgme_ctx_t ctx); /* Get the string describing protocol PROTO, or NULL if invalid. */ const char *gpgme_get_protocol_name (gpgme_protocol_t proto); /* If YES is non-zero, enable armor mode in CTX, disable it otherwise. */ void gpgme_set_armor (gpgme_ctx_t ctx, int yes); /* Return non-zero if armor mode is set in CTX. */ int gpgme_get_armor (gpgme_ctx_t ctx); /* If YES is non-zero, enable text mode in CTX, disable it otherwise. */ void gpgme_set_textmode (gpgme_ctx_t ctx, int yes); /* Return non-zero if text mode is set in CTX. */ int gpgme_get_textmode (gpgme_ctx_t ctx); /* Use whatever the default of the backend crypto engine is. */ #define GPGME_INCLUDE_CERTS_DEFAULT -256 /* Include up to NR_OF_CERTS certificates in an S/MIME message. */ void gpgme_set_include_certs (gpgme_ctx_t ctx, int nr_of_certs); /* Return the number of certs to include in an S/MIME message. */ int gpgme_get_include_certs (gpgme_ctx_t ctx); /* Set keylist mode in CTX to MODE. */ gpgme_error_t gpgme_set_keylist_mode (gpgme_ctx_t ctx, gpgme_keylist_mode_t mode); /* Get keylist mode in CTX. */ gpgme_keylist_mode_t gpgme_get_keylist_mode (gpgme_ctx_t ctx); /* Set the passphrase callback function in CTX to CB. HOOK_VALUE is passed as first argument to the passphrase callback function. */ void gpgme_set_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t cb, void *hook_value); /* Get the current passphrase callback function in *CB and the current hook value in *HOOK_VALUE. */ void gpgme_get_passphrase_cb (gpgme_ctx_t ctx, gpgme_passphrase_cb_t *cb, void **hook_value); /* Set the progress callback function in CTX to CB. HOOK_VALUE is passed as first argument to the progress callback function. */ void gpgme_set_progress_cb (gpgme_ctx_t c, gpgme_progress_cb_t cb, void *hook_value); /* Get the current progress callback function in *CB and the current hook value in *HOOK_VALUE. */ void gpgme_get_progress_cb (gpgme_ctx_t ctx, gpgme_progress_cb_t *cb, void **hook_value); /* This function sets the locale for the context CTX, or the default locale if CTX is a null pointer. */ gpgme_error_t gpgme_set_locale (gpgme_ctx_t ctx, int category, const char *value); /* Get the information about the configured engines. A pointer to the first engine in the statically allocated linked list is returned. The returned data is valid until the next gpgme_ctx_set_engine_info. */ gpgme_engine_info_t gpgme_ctx_get_engine_info (gpgme_ctx_t ctx); /* Set the engine info for the context CTX, protocol PROTO, to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_ctx_set_engine_info (gpgme_ctx_t ctx, gpgme_protocol_t proto, const char *file_name, const char *home_dir); /* Return a statically allocated string with the name of the public key algorithm ALGO, or NULL if that name is not known. */ const char *gpgme_pubkey_algo_name (gpgme_pubkey_algo_t algo); /* Return a statically allocated string with the name of the hash algorithm ALGO, or NULL if that name is not known. */ const char *gpgme_hash_algo_name (gpgme_hash_algo_t algo); /* Delete all signers from CTX. */ void gpgme_signers_clear (gpgme_ctx_t ctx); /* Add KEY to list of signers in CTX. */ gpgme_error_t gpgme_signers_add (gpgme_ctx_t ctx, const gpgme_key_t key); /* Return the SEQth signer's key in CTX. */ gpgme_key_t gpgme_signers_enum (const gpgme_ctx_t ctx, int seq); /* Retrieve the signature status of signature IDX in CTX after a successful verify operation in R_STAT (if non-null). The creation time stamp of the signature is returned in R_CREATED (if non-null). The function returns a string containing the fingerprint. Deprecated, use verify result directly. */ const char *gpgme_get_sig_status (gpgme_ctx_t ctx, int idx, _gpgme_sig_stat_t *r_stat, time_t *r_created) _GPGME_DEPRECATED; /* Retrieve certain attributes of a signature. IDX is the index number of the signature after a successful verify operation. WHAT is an attribute where GPGME_ATTR_EXPIRE is probably the most useful one. WHATIDX is to be passed as 0 for most attributes . */ unsigned long gpgme_get_sig_ulong_attr (gpgme_ctx_t c, int idx, _gpgme_attr_t what, int whatidx) _GPGME_DEPRECATED; const char *gpgme_get_sig_string_attr (gpgme_ctx_t c, int idx, _gpgme_attr_t what, int whatidx) _GPGME_DEPRECATED; /* Get the key used to create signature IDX in CTX and return it in R_KEY. */ gpgme_error_t gpgme_get_sig_key (gpgme_ctx_t ctx, int idx, gpgme_key_t *r_key) _GPGME_DEPRECATED; /* Clear all notation data from the context. */ void gpgme_sig_notation_clear (gpgme_ctx_t ctx); /* Add the human-readable notation data with name NAME and value VALUE to the context CTX, using the flags FLAGS. If NAME is NULL, then VALUE should be a policy URL. The flag GPGME_SIG_NOTATION_HUMAN_READABLE is forced to be true for notation data, and false for policy URLs. */ gpgme_error_t gpgme_sig_notation_add (gpgme_ctx_t ctx, const char *name, const char *value, gpgme_sig_notation_flags_t flags); /* Get the sig notations for this context. */ gpgme_sig_notation_t gpgme_sig_notation_get (gpgme_ctx_t ctx); /* Run control. */ /* The type of an I/O callback function. */ typedef gpgme_error_t (*gpgme_io_cb_t) (void *data, int fd); /* The type of a function that can register FNC as the I/O callback function for the file descriptor FD with direction dir (0: for writing, 1: for reading). FNC_DATA should be passed as DATA to FNC. The function should return a TAG suitable for the corresponding gpgme_remove_io_cb_t, and an error value. */ typedef gpgme_error_t (*gpgme_register_io_cb_t) (void *data, int fd, int dir, gpgme_io_cb_t fnc, void *fnc_data, void **tag); /* The type of a function that can remove a previously registered I/O callback function given TAG as returned by the register function. */ typedef void (*gpgme_remove_io_cb_t) (void *tag); typedef enum { GPGME_EVENT_START, GPGME_EVENT_DONE, GPGME_EVENT_NEXT_KEY, GPGME_EVENT_NEXT_TRUSTITEM } gpgme_event_io_t; struct gpgme_io_event_done_data { /* A fatal IPC error or an operational error in state-less protocols. */ gpgme_error_t err; /* An operational errors in session-based protocols. */ gpgme_error_t op_err; }; typedef struct gpgme_io_event_done_data *gpgme_io_event_done_data_t; /* The type of a function that is called when a context finished an operation. */ typedef void (*gpgme_event_io_cb_t) (void *data, gpgme_event_io_t type, void *type_data); struct gpgme_io_cbs { gpgme_register_io_cb_t add; void *add_priv; gpgme_remove_io_cb_t remove; gpgme_event_io_cb_t event; void *event_priv; }; typedef struct gpgme_io_cbs *gpgme_io_cbs_t; /* Set the I/O callback functions in CTX to IO_CBS. */ void gpgme_set_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs); /* Get the current I/O callback functions. */ void gpgme_get_io_cbs (gpgme_ctx_t ctx, gpgme_io_cbs_t io_cbs); /* Wrappers around the internal I/O functions for use with gpgme_passphrase_cb_t and gpgme_edit_cb_t. */ ssize_t gpgme_io_read (int fd, void *buffer, size_t count); ssize_t gpgme_io_write (int fd, const void *buffer, size_t count); /* Process the pending operation and, if HANG is non-zero, wait for the pending operation to finish. */ gpgme_ctx_t gpgme_wait (gpgme_ctx_t ctx, gpgme_error_t *status, int hang); gpgme_ctx_t gpgme_wait_ext (gpgme_ctx_t ctx, gpgme_error_t *status, gpgme_error_t *op_err, int hang); /* Functions to handle data objects. */ /* Read up to SIZE bytes into buffer BUFFER from the data object with the handle HANDLE. Return the number of characters read, 0 on EOF and -1 on error. If an error occurs, errno is set. */ typedef ssize_t (*gpgme_data_read_cb_t) (void *handle, void *buffer, size_t size); /* Write up to SIZE bytes from buffer BUFFER to the data object with the handle HANDLE. Return the number of characters written, or -1 on error. If an error occurs, errno is set. */ typedef ssize_t (*gpgme_data_write_cb_t) (void *handle, const void *buffer, size_t size); /* Set the current position from where the next read or write starts in the data object with the handle HANDLE to OFFSET, relativ to WHENCE. */ typedef off_t (*gpgme_data_seek_cb_t) (void *handle, off_t offset, int whence); /* Close the data object with the handle DL. */ typedef void (*gpgme_data_release_cb_t) (void *handle); struct gpgme_data_cbs { gpgme_data_read_cb_t read; gpgme_data_write_cb_t write; gpgme_data_seek_cb_t seek; gpgme_data_release_cb_t release; }; typedef struct gpgme_data_cbs *gpgme_data_cbs_t; /* Read up to SIZE bytes into buffer BUFFER from the data object with the handle DH. Return the number of characters read, 0 on EOF and -1 on error. If an error occurs, errno is set. */ ssize_t gpgme_data_read (gpgme_data_t dh, void *buffer, size_t size); /* Write up to SIZE bytes from buffer BUFFER to the data object with the handle DH. Return the number of characters written, or -1 on error. If an error occurs, errno is set. */ ssize_t gpgme_data_write (gpgme_data_t dh, const void *buffer, size_t size); /* Set the current position from where the next read or write starts in the data object with the handle DH to OFFSET, relativ to WHENCE. */ off_t gpgme_data_seek (gpgme_data_t dh, off_t offset, int whence); /* Create a new data buffer and return it in R_DH. */ gpgme_error_t gpgme_data_new (gpgme_data_t *r_dh); /* Destroy the data buffer DH. */ void gpgme_data_release (gpgme_data_t dh); /* Create a new data buffer filled with SIZE bytes starting from BUFFER. If COPY is zero, copying is delayed until necessary, and the data is taken from the original location when needed. */ gpgme_error_t gpgme_data_new_from_mem (gpgme_data_t *r_dh, const char *buffer, size_t size, int copy); /* Destroy the data buffer DH and return a pointer to its content. The memory has be to released with gpgme_free() by the user. It's size is returned in R_LEN. */ char *gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len); /* Release the memory returned by gpgme_data_release_and_get_mem(). */ void gpgme_free (void *buffer); gpgme_error_t gpgme_data_new_from_cbs (gpgme_data_t *dh, gpgme_data_cbs_t cbs, void *handle); gpgme_error_t gpgme_data_new_from_fd (gpgme_data_t *dh, int fd); gpgme_error_t gpgme_data_new_from_stream (gpgme_data_t *dh, FILE *stream); /* Return the encoding attribute of the data buffer DH */ gpgme_data_encoding_t gpgme_data_get_encoding (gpgme_data_t dh); /* Set the encoding attribute of data buffer DH to ENC */ gpgme_error_t gpgme_data_set_encoding (gpgme_data_t dh, gpgme_data_encoding_t enc); /* Get the file name associated with the data object with handle DH, or NULL if there is none. */ char *gpgme_data_get_file_name (gpgme_data_t dh); /* Set the file name associated with the data object with handle DH to FILE_NAME. */ gpgme_error_t gpgme_data_set_file_name (gpgme_data_t dh, const char *file_name); /* Create a new data buffer which retrieves the data from the callback function READ_CB. Deprecated, please use gpgme_data_new_from_cbs instead. */ gpgme_error_t gpgme_data_new_with_read_cb (gpgme_data_t *r_dh, int (*read_cb) (void*,char *, size_t,size_t*), void *read_cb_value) _GPGME_DEPRECATED; /* Create a new data buffer filled with the content of file FNAME. COPY must be non-zero. For delayed read, please use gpgme_data_new_from_fd or gpgme_data_new_from stream instead. */ gpgme_error_t gpgme_data_new_from_file (gpgme_data_t *r_dh, const char *fname, int copy); /* Create a new data buffer filled with LENGTH bytes starting from OFFSET within the file FNAME or stream FP (exactly one must be non-zero). */ gpgme_error_t gpgme_data_new_from_filepart (gpgme_data_t *r_dh, const char *fname, FILE *fp, off_t offset, size_t length); /* Reset the read pointer in DH. Deprecated, please use gpgme_data_seek instead. */ gpgme_error_t gpgme_data_rewind (gpgme_data_t dh) _GPGME_DEPRECATED; /* Key and trust functions. */ /* Get the key with the fingerprint FPR from the crypto backend. If SECRET is true, get the secret key. */ gpgme_error_t gpgme_get_key (gpgme_ctx_t ctx, const char *fpr, gpgme_key_t *r_key, int secret); /* Acquire a reference to KEY. */ void gpgme_key_ref (gpgme_key_t key); /* Release a reference to KEY. If this was the last one the key is destroyed. */ void gpgme_key_unref (gpgme_key_t key); void gpgme_key_release (gpgme_key_t key); /* Return the value of the attribute WHAT of KEY, which has to be representable by a string. IDX specifies the sub key or user ID for attributes related to sub keys or user IDs. Deprecated, use key structure directly instead. */ const char *gpgme_key_get_string_attr (gpgme_key_t key, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Return the value of the attribute WHAT of KEY, which has to be representable by an unsigned integer. IDX specifies the sub key or user ID for attributes related to sub keys or user IDs. Deprecated, use key structure directly instead. */ unsigned long gpgme_key_get_ulong_attr (gpgme_key_t key, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Return the value of the attribute WHAT of a signature on user ID UID_IDX in KEY, which has to be representable by a string. IDX specifies the signature. Deprecated, use key structure directly instead. */ const char *gpgme_key_sig_get_string_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Return the value of the attribute WHAT of a signature on user ID UID_IDX in KEY, which has to be representable by an unsigned integer string. IDX specifies the signature. Deprecated, use key structure directly instead. */ unsigned long gpgme_key_sig_get_ulong_attr (gpgme_key_t key, int uid_idx, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Crypto Operations. */ /* Cancel a pending asynchronous operation. */ gpgme_error_t gpgme_cancel (gpgme_ctx_t ctx); /* Cancel a pending operation asynchronously. */ gpgme_error_t gpgme_cancel_async (gpgme_ctx_t ctx); struct _gpgme_invalid_key { struct _gpgme_invalid_key *next; char *fpr; gpgme_error_t reason; }; typedef struct _gpgme_invalid_key *gpgme_invalid_key_t; /* Encryption. */ struct _gpgme_op_encrypt_result { /* The list of invalid recipients. */ gpgme_invalid_key_t invalid_recipients; }; typedef struct _gpgme_op_encrypt_result *gpgme_encrypt_result_t; /* Retrieve a pointer to the result of the encrypt operation. */ gpgme_encrypt_result_t gpgme_op_encrypt_result (gpgme_ctx_t ctx); /* The valid encryption flags. */ typedef enum { GPGME_ENCRYPT_ALWAYS_TRUST = 1, GPGME_ENCRYPT_NO_ENCRYPT_TO = 2, GPGME_ENCRYPT_PREPARE = 4, GPGME_ENCRYPT_EXPECT_SIGN = 8 } gpgme_encrypt_flags_t; /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. */ gpgme_error_t gpgme_op_encrypt_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher); gpgme_error_t gpgme_op_encrypt (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher); /* Encrypt plaintext PLAIN within CTX for the recipients RECP and store the resulting ciphertext in CIPHER. Also sign the ciphertext with the signers in CTX. */ gpgme_error_t gpgme_op_encrypt_sign_start (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher); gpgme_error_t gpgme_op_encrypt_sign (gpgme_ctx_t ctx, gpgme_key_t recp[], gpgme_encrypt_flags_t flags, gpgme_data_t plain, gpgme_data_t cipher); /* Decryption. */ struct _gpgme_recipient { struct _gpgme_recipient *next; /* The key ID of key for which the text was encrypted. */ char *keyid; /* Internal to GPGME, do not use. */ char _keyid[16 + 1]; /* The public key algorithm of the recipient key. */ gpgme_pubkey_algo_t pubkey_algo; /* The status of the recipient. */ gpgme_error_t status; }; typedef struct _gpgme_recipient *gpgme_recipient_t; struct _gpgme_op_decrypt_result { char *unsupported_algorithm; /* Key should not have been used for encryption. */ unsigned int wrong_key_usage : 1; /* Internal to GPGME, do not use. */ int _unused : 31; gpgme_recipient_t recipients; /* The original file name of the plaintext message, if available. */ char *file_name; }; typedef struct _gpgme_op_decrypt_result *gpgme_decrypt_result_t; /* Retrieve a pointer to the result of the decrypt operation. */ gpgme_decrypt_result_t gpgme_op_decrypt_result (gpgme_ctx_t ctx); /* Decrypt ciphertext CIPHER within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); gpgme_error_t gpgme_op_decrypt (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); /* Decrypt ciphertext CIPHER and make a signature verification within CTX and store the resulting plaintext in PLAIN. */ gpgme_error_t gpgme_op_decrypt_verify_start (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); gpgme_error_t gpgme_op_decrypt_verify (gpgme_ctx_t ctx, gpgme_data_t cipher, gpgme_data_t plain); /* Signing. */ struct _gpgme_new_signature { struct _gpgme_new_signature *next; /* The type of the signature. */ gpgme_sig_mode_t type; /* The public key algorithm used to create the signature. */ gpgme_pubkey_algo_t pubkey_algo; /* The hash algorithm used to create the signature. */ gpgme_hash_algo_t hash_algo; /* Internal to GPGME, do not use. Must be set to the same value as CLASS below. */ unsigned long _obsolete_class; /* Signature creation time. */ long int timestamp; /* The fingerprint of the signature. */ char *fpr; #ifdef __cplusplus unsigned int _obsolete_class_2; #else /* Must be set to SIG_CLASS below. */ unsigned int class _GPGME_DEPRECATED_OUTSIDE_GPGME; #endif /* Crypto backend specific signature class. */ unsigned int sig_class; }; typedef struct _gpgme_new_signature *gpgme_new_signature_t; struct _gpgme_op_sign_result { /* The list of invalid signers. */ gpgme_invalid_key_t invalid_signers; gpgme_new_signature_t signatures; }; typedef struct _gpgme_op_sign_result *gpgme_sign_result_t; /* Retrieve a pointer to the result of the signing operation. */ gpgme_sign_result_t gpgme_op_sign_result (gpgme_ctx_t ctx); /* Sign the plaintext PLAIN and store the signature in SIG. */ gpgme_error_t gpgme_op_sign_start (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode); gpgme_error_t gpgme_op_sign (gpgme_ctx_t ctx, gpgme_data_t plain, gpgme_data_t sig, gpgme_sig_mode_t mode); /* Verify. */ /* Flags used for the SUMMARY field in a gpgme_signature_t. */ typedef enum { GPGME_SIGSUM_VALID = 0x0001, /* The signature is fully valid. */ GPGME_SIGSUM_GREEN = 0x0002, /* The signature is good. */ GPGME_SIGSUM_RED = 0x0004, /* The signature is bad. */ GPGME_SIGSUM_KEY_REVOKED = 0x0010, /* One key has been revoked. */ GPGME_SIGSUM_KEY_EXPIRED = 0x0020, /* One key has expired. */ GPGME_SIGSUM_SIG_EXPIRED = 0x0040, /* The signature has expired. */ GPGME_SIGSUM_KEY_MISSING = 0x0080, /* Can't verify: key missing. */ GPGME_SIGSUM_CRL_MISSING = 0x0100, /* CRL not available. */ GPGME_SIGSUM_CRL_TOO_OLD = 0x0200, /* Available CRL is too old. */ GPGME_SIGSUM_BAD_POLICY = 0x0400, /* A policy was not met. */ GPGME_SIGSUM_SYS_ERROR = 0x0800 /* A system error occured. */ } gpgme_sigsum_t; struct _gpgme_signature { struct _gpgme_signature *next; /* A summary of the signature status. */ gpgme_sigsum_t summary; /* The fingerprint or key ID of the signature. */ char *fpr; /* The status of the signature. */ gpgme_error_t status; /* Notation data and policy URLs. */ gpgme_sig_notation_t notations; /* Signature creation time. */ unsigned long timestamp; /* Signature exipration time or 0. */ unsigned long exp_timestamp; /* Key should not have been used for signing. */ unsigned int wrong_key_usage : 1; /* PKA status: 0 = not available, 1 = bad, 2 = okay, 3 = RFU. */ unsigned int pka_trust : 2; /* Validity has been verified using the chain model. */ unsigned int chain_model : 1; /* Internal to GPGME, do not use. */ int _unused : 28; gpgme_validity_t validity; gpgme_error_t validity_reason; /* The public key algorithm used to create the signature. */ gpgme_pubkey_algo_t pubkey_algo; /* The hash algorithm used to create the signature. */ gpgme_hash_algo_t hash_algo; /* The mailbox from the PKA information or NULL. */ char *pka_address; }; typedef struct _gpgme_signature *gpgme_signature_t; struct _gpgme_op_verify_result { gpgme_signature_t signatures; /* The original file name of the plaintext message, if available. */ char *file_name; }; typedef struct _gpgme_op_verify_result *gpgme_verify_result_t; /* Retrieve a pointer to the result of the verify operation. */ gpgme_verify_result_t gpgme_op_verify_result (gpgme_ctx_t ctx); /* Verify within CTX that SIG is a valid signature for TEXT. */ gpgme_error_t gpgme_op_verify_start (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext); gpgme_error_t gpgme_op_verify (gpgme_ctx_t ctx, gpgme_data_t sig, gpgme_data_t signed_text, gpgme_data_t plaintext); /* Import. */ /* The key was new. */ #define GPGME_IMPORT_NEW 1 /* The key contained new user IDs. */ #define GPGME_IMPORT_UID 2 /* The key contained new signatures. */ #define GPGME_IMPORT_SIG 4 /* The key contained new sub keys. */ #define GPGME_IMPORT_SUBKEY 8 /* The key contained a secret key. */ #define GPGME_IMPORT_SECRET 16 struct _gpgme_import_status { struct _gpgme_import_status *next; /* Fingerprint. */ char *fpr; /* If a problem occured, the reason why the key could not be imported. Otherwise GPGME_No_Error. */ gpgme_error_t result; /* The result of the import, the GPGME_IMPORT_* values bit-wise ORed. 0 means the key was already known and no new components have been added. */ unsigned int status; }; typedef struct _gpgme_import_status *gpgme_import_status_t; /* Import. */ struct _gpgme_op_import_result { /* Number of considered keys. */ int considered; /* Keys without user ID. */ int no_user_id; /* Imported keys. */ int imported; /* Imported RSA keys. */ int imported_rsa; /* Unchanged keys. */ int unchanged; /* Number of new user ids. */ int new_user_ids; /* Number of new sub keys. */ int new_sub_keys; /* Number of new signatures. */ int new_signatures; /* Number of new revocations. */ int new_revocations; /* Number of secret keys read. */ int secret_read; /* Number of secret keys imported. */ int secret_imported; /* Number of secret keys unchanged. */ int secret_unchanged; /* Number of new keys skipped. */ int skipped_new_keys; /* Number of keys not imported. */ int not_imported; /* List of keys for which an import was attempted. */ gpgme_import_status_t imports; }; typedef struct _gpgme_op_import_result *gpgme_import_result_t; /* Retrieve a pointer to the result of the import operation. */ gpgme_import_result_t gpgme_op_import_result (gpgme_ctx_t ctx); /* Import the key in KEYDATA into the keyring. */ gpgme_error_t gpgme_op_import_start (gpgme_ctx_t ctx, gpgme_data_t keydata); gpgme_error_t gpgme_op_import (gpgme_ctx_t ctx, gpgme_data_t keydata); gpgme_error_t gpgme_op_import_ext (gpgme_ctx_t ctx, gpgme_data_t keydata, int *nr) _GPGME_DEPRECATED; /* Import the keys from the array KEYS into the keyring. */ gpgme_error_t gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[]); gpgme_error_t gpgme_op_import_keys (gpgme_ctx_t ctx, gpgme_key_t keys[]); /* Export the keys found by PATTERN into KEYDATA. */ gpgme_error_t gpgme_op_export_start (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export (gpgme_ctx_t ctx, const char *pattern, gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export_ext_start (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export_ext (gpgme_ctx_t ctx, const char *pattern[], gpgme_export_mode_t mode, gpgme_data_t keydata); /* Export the keys from the array KEYS into KEYDATA. */ gpgme_error_t gpgme_op_export_keys_start (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata); gpgme_error_t gpgme_op_export_keys (gpgme_ctx_t ctx, gpgme_key_t keys[], gpgme_export_mode_t mode, gpgme_data_t keydata); /* Key generation. */ struct _gpgme_op_genkey_result { /* A primary key was generated. */ unsigned int primary : 1; /* A sub key was generated. */ unsigned int sub : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 30; /* The fingerprint of the generated key. */ char *fpr; }; typedef struct _gpgme_op_genkey_result *gpgme_genkey_result_t; /* Generate a new keypair and add it to the keyring. PUBKEY and SECKEY should be null for now. PARMS specifies what keys should be generated. */ gpgme_error_t gpgme_op_genkey_start (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey); gpgme_error_t gpgme_op_genkey (gpgme_ctx_t ctx, const char *parms, gpgme_data_t pubkey, gpgme_data_t seckey); /* Retrieve a pointer to the result of the genkey operation. */ gpgme_genkey_result_t gpgme_op_genkey_result (gpgme_ctx_t ctx); /* Delete KEY from the keyring. If ALLOW_SECRET is non-zero, secret keys are also deleted. */ gpgme_error_t gpgme_op_delete_start (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret); gpgme_error_t gpgme_op_delete (gpgme_ctx_t ctx, const gpgme_key_t key, int allow_secret); /* Edit the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out); gpgme_error_t gpgme_op_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out); /* Edit the card for the key KEY. Send status and command requests to FNC and output of edit commands to OUT. */ gpgme_error_t gpgme_op_card_edit_start (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out); gpgme_error_t gpgme_op_card_edit (gpgme_ctx_t ctx, gpgme_key_t key, gpgme_edit_cb_t fnc, void *fnc_value, gpgme_data_t out); /* Key management functions. */ struct _gpgme_op_keylist_result { unsigned int truncated : 1; /* Internal to GPGME, do not use. */ unsigned int _unused : 31; }; typedef struct _gpgme_op_keylist_result *gpgme_keylist_result_t; /* Retrieve a pointer to the result of the key listing operation. */ gpgme_keylist_result_t gpgme_op_keylist_result (gpgme_ctx_t ctx); /* Start a keylist operation within CTX, searching for keys which match PATTERN. If SECRET_ONLY is true, only secret keys are returned. */ gpgme_error_t gpgme_op_keylist_start (gpgme_ctx_t ctx, const char *pattern, int secret_only); gpgme_error_t gpgme_op_keylist_ext_start (gpgme_ctx_t ctx, const char *pattern[], int secret_only, int reserved); /* Return the next key from the keylist in R_KEY. */ gpgme_error_t gpgme_op_keylist_next (gpgme_ctx_t ctx, gpgme_key_t *r_key); /* Terminate a pending keylist operation within CTX. */ gpgme_error_t gpgme_op_keylist_end (gpgme_ctx_t ctx); /* Change the passphrase for KEY. FLAGS is reserved for future use and must be passed as 0. */ gpgme_error_t gpgme_op_passwd_start (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags); gpgme_error_t gpgme_op_passwd (gpgme_ctx_t ctx, gpgme_key_t key, unsigned int flags); /* Trust items and operations. */ struct _gpgme_trust_item { /* Internal to GPGME, do not use. */ unsigned int _refs; /* The key ID to which the trust item belongs. */ char *keyid; /* Internal to GPGME, do not use. */ char _keyid[16 + 1]; /* The type of the trust item, 1 refers to a key, 2 to a user ID. */ int type; /* The trust level. */ int level; /* The owner trust if TYPE is 1. */ char *owner_trust; /* Internal to GPGME, do not use. */ char _owner_trust[2]; /* The calculated validity. */ char *validity; /* Internal to GPGME, do not use. */ char _validity[2]; /* The user name if TYPE is 2. */ char *name; }; typedef struct _gpgme_trust_item *gpgme_trust_item_t; /* Start a trustlist operation within CTX, searching for trust items which match PATTERN. */ gpgme_error_t gpgme_op_trustlist_start (gpgme_ctx_t ctx, const char *pattern, int max_level); /* Return the next trust item from the trustlist in R_ITEM. */ gpgme_error_t gpgme_op_trustlist_next (gpgme_ctx_t ctx, gpgme_trust_item_t *r_item); /* Terminate a pending trustlist operation within CTX. */ gpgme_error_t gpgme_op_trustlist_end (gpgme_ctx_t ctx); /* Acquire a reference to ITEM. */ void gpgme_trust_item_ref (gpgme_trust_item_t item); /* Release a reference to ITEM. If this was the last one the trust item is destroyed. */ void gpgme_trust_item_unref (gpgme_trust_item_t item); /* Release the trust item ITEM. Deprecated, use gpgme_trust_item_unref. */ void gpgme_trust_item_release (gpgme_trust_item_t item) _GPGME_DEPRECATED; /* Return the value of the attribute WHAT of ITEM, which has to be representable by a string. Deprecated, use trust item structure directly. */ const char *gpgme_trust_item_get_string_attr (gpgme_trust_item_t item, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Return the value of the attribute WHAT of KEY, which has to be representable by an integer. IDX specifies a running index if the attribute appears more than once in the key. Deprecated, use trust item structure directly. */ int gpgme_trust_item_get_int_attr (gpgme_trust_item_t item, _gpgme_attr_t what, const void *reserved, int idx) _GPGME_DEPRECATED; /* Return the auditlog for the current session. This may be called after a successful or failed operation. If no audit log is available GPG_ERR_NO_DATA is returned. */ gpgme_error_t gpgme_op_getauditlog_start (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags); gpgme_error_t gpgme_op_getauditlog (gpgme_ctx_t ctx, gpgme_data_t output, unsigned int flags); /* Low-level Assuan protocol access. */ typedef gpgme_error_t (*gpgme_assuan_data_cb_t) (void *opaque, const void *data, size_t datalen); typedef gpgme_error_t (*gpgme_assuan_inquire_cb_t) (void *opaque, const char *name, const char *args, gpgme_data_t *r_data); typedef gpgme_error_t (*gpgme_assuan_status_cb_t) (void *opaque, const char *status, const char *args); /* Send the Assuan COMMAND and return results via the callbacks. Asynchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_start (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t stat_cb, void *stat_cb_value); /* Send the Assuan COMMAND and return results via the callbacks. Synchronous variant. */ gpgme_error_t gpgme_op_assuan_transact_ext (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t stat_cb, void *stat_cb_value, gpgme_error_t *op_err); /* Compat. */ struct _gpgme_op_assuan_result { /* Deprecated. Use the second value in a DONE event or the synchronous variant gpgme_op_assuan_transact_ext. */ gpgme_error_t err _GPGME_DEPRECATED_OUTSIDE_GPGME; }; typedef struct _gpgme_op_assuan_result *gpgme_assuan_result_t; /* Return the result of the last Assuan command. */ gpgme_assuan_result_t gpgme_op_assuan_result (gpgme_ctx_t ctx) _GPGME_DEPRECATED; gpgme_error_t gpgme_op_assuan_transact (gpgme_ctx_t ctx, const char *command, gpgme_assuan_data_cb_t data_cb, void *data_cb_value, gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value, gpgme_assuan_status_cb_t status_cb, void *status_cb_value) _GPGME_DEPRECATED; /* Crypto container support. */ struct _gpgme_op_vfs_mount_result { char *mount_dir; }; typedef struct _gpgme_op_vfs_mount_result *gpgme_vfs_mount_result_t; gpgme_vfs_mount_result_t gpgme_op_vfs_mount_result (gpgme_ctx_t ctx); /* The container is automatically unmounted when the context is reset or destroyed. Transmission errors are returned directly, operational errors are returned in OP_ERR. */ gpgme_error_t gpgme_op_vfs_mount (gpgme_ctx_t ctx, const char *container_file, const char *mount_dir, unsigned int flags, gpgme_error_t *op_err); gpgme_error_t gpgme_op_vfs_create (gpgme_ctx_t ctx, gpgme_key_t recp[], const char *container_file, unsigned int flags, gpgme_error_t *op_err); /* Interface to gpgconf(1). */ /* The expert level at which a configuration option or group of options should be displayed. See the gpgconf(1) documentation for more details. */ typedef enum { GPGME_CONF_BASIC = 0, GPGME_CONF_ADVANCED = 1, GPGME_CONF_EXPERT = 2, GPGME_CONF_INVISIBLE = 3, GPGME_CONF_INTERNAL = 4 } gpgme_conf_level_t; /* The data type of a configuration option argument. See the gpgconf(1) documentation for more details. */ typedef enum { /* Basic types. */ GPGME_CONF_NONE = 0, GPGME_CONF_STRING = 1, GPGME_CONF_INT32 = 2, GPGME_CONF_UINT32 = 3, /* Complex types. */ GPGME_CONF_FILENAME = 32, GPGME_CONF_LDAP_SERVER = 33, GPGME_CONF_KEY_FPR = 34, GPGME_CONF_PUB_KEY = 35, GPGME_CONF_SEC_KEY = 36, GPGME_CONF_ALIAS_LIST = 37 } gpgme_conf_type_t; /* For now, compatibility. */ #define GPGME_CONF_PATHNAME GPGME_CONF_FILENAME /* This represents a single argument for a configuration option. Which of the members of value is used depends on the ALT_TYPE. */ typedef struct gpgme_conf_arg { struct gpgme_conf_arg *next; /* True if the option appears without an (optional) argument. */ unsigned int no_arg; union { unsigned int count; unsigned int uint32; int int32; char *string; } value; } *gpgme_conf_arg_t; /* The flags of a configuration option. See the gpg-conf documentation for details. */ #define GPGME_CONF_GROUP (1 << 0) #define GPGME_CONF_OPTIONAL (1 << 1) #define GPGME_CONF_LIST (1 << 2) #define GPGME_CONF_RUNTIME (1 << 3) #define GPGME_CONF_DEFAULT (1 << 4) #define GPGME_CONF_DEFAULT_DESC (1 << 5) #define GPGME_CONF_NO_ARG_DESC (1 << 6) #define GPGME_CONF_NO_CHANGE (1 << 7) /* The representation of a single configuration option. See the gpg-conf documentation for details. */ typedef struct gpgme_conf_opt { struct gpgme_conf_opt *next; /* The option name. */ char *name; /* The flags for this option. */ unsigned int flags; /* The level of this option. */ gpgme_conf_level_t level; /* The localized description of this option. */ char *description; /* The type and alternate type of this option. */ gpgme_conf_type_t type; gpgme_conf_type_t alt_type; /* The localized (short) name of the argument, if any. */ char *argname; /* The default value. */ gpgme_conf_arg_t default_value; char *default_description; /* The default value if the option is not set. */ gpgme_conf_arg_t no_arg_value; char *no_arg_description; /* The current value if the option is set. */ gpgme_conf_arg_t value; /* The new value, if any. NULL means reset to default. */ int change_value; gpgme_conf_arg_t new_value; /* Free for application use. */ void *user_data; } *gpgme_conf_opt_t; /* The representation of a component that can be configured. See the gpg-conf documentation for details. */ typedef struct gpgme_conf_comp { struct gpgme_conf_comp *next; /* Internal to GPGME, do not use! */ gpgme_conf_opt_t *_last_opt_p; /* The component name. */ char *name; /* A human-readable description for the component. */ char *description; /* The program name (an absolute path to the program). */ char *program_name; /* A linked list of options for this component. */ struct gpgme_conf_opt *options; } *gpgme_conf_comp_t; /* Allocate a new gpgme_conf_arg_t. If VALUE is NULL, a "no arg default" is prepared. If type is a string type, VALUE should point to the string. Else, it should point to an unsigned or signed integer respectively. */ gpgme_error_t gpgme_conf_arg_new (gpgme_conf_arg_t *arg_p, gpgme_conf_type_t type, const void *value); /* This also releases all chained argument structures! */ void gpgme_conf_arg_release (gpgme_conf_arg_t arg, gpgme_conf_type_t type); /* Register a change for the value of OPT to ARG. If RESET is 1 (do not use any values but 0 or 1), ARG is ignored and the option is not changed (reverting a previous change). Otherwise, if ARG is NULL, the option is cleared or reset to its default. */ gpgme_error_t gpgme_conf_opt_change (gpgme_conf_opt_t opt, int reset, gpgme_conf_arg_t arg); /* Release a set of configurations. */ void gpgme_conf_release (gpgme_conf_comp_t conf); /* Retrieve the current configurations. */ gpgme_error_t gpgme_op_conf_load (gpgme_ctx_t ctx, gpgme_conf_comp_t *conf_p); /* Save the configuration of component comp. This function does not follow chained components! */ gpgme_error_t gpgme_op_conf_save (gpgme_ctx_t ctx, gpgme_conf_comp_t comp); /* UIServer support. */ /* Create a dummy key to specify an email address. */ gpgme_error_t gpgme_key_from_uid (gpgme_key_t *key, const char *name); /* Various functions. */ /* Check that the library fulfills the version requirement. Note: This is here only for the case where a user takes a pointer from the old version of this function. The new version and macro for run-time checks are below. */ const char *gpgme_check_version (const char *req_version); /* Check that the library fulfills the version requirement and check for struct layout mismatch involving bitfields. */ const char *gpgme_check_version_internal (const char *req_version, size_t offset_sig_validity); #define gpgme_check_version(req_version) \ gpgme_check_version_internal (req_version, \ offsetof (struct _gpgme_signature, validity)) /* Get the information about the configured and installed engines. A pointer to the first engine in the statically allocated linked list is returned in *INFO. If an error occurs, it is returned. The returned data is valid until the next gpgme_set_engine_info. */ gpgme_error_t gpgme_get_engine_info (gpgme_engine_info_t *engine_info); /* Set the default engine info for the protocol PROTO to the file name FILE_NAME and the home directory HOME_DIR. */ gpgme_error_t gpgme_set_engine_info (gpgme_protocol_t proto, const char *file_name, const char *home_dir); /* Engine support functions. */ /* Verify that the engine implementing PROTO is installed and available. */ gpgme_error_t gpgme_engine_check_version (gpgme_protocol_t proto); void gpgme_result_ref (void *result); void gpgme_result_unref (void *result); /* Deprecated types. */ typedef gpgme_ctx_t GpgmeCtx _GPGME_DEPRECATED; typedef gpgme_data_t GpgmeData _GPGME_DEPRECATED; typedef gpgme_error_t GpgmeError _GPGME_DEPRECATED; typedef gpgme_data_encoding_t GpgmeDataEncoding _GPGME_DEPRECATED; typedef gpgme_pubkey_algo_t GpgmePubKeyAlgo _GPGME_DEPRECATED; typedef gpgme_hash_algo_t GpgmeHashAlgo _GPGME_DEPRECATED; typedef gpgme_sig_stat_t GpgmeSigStat _GPGME_DEPRECATED; typedef gpgme_sig_mode_t GpgmeSigMode _GPGME_DEPRECATED; typedef gpgme_attr_t GpgmeAttr _GPGME_DEPRECATED; typedef gpgme_validity_t GpgmeValidity _GPGME_DEPRECATED; typedef gpgme_protocol_t GpgmeProtocol _GPGME_DEPRECATED; typedef gpgme_engine_info_t GpgmeEngineInfo _GPGME_DEPRECATED; typedef gpgme_subkey_t GpgmeSubkey _GPGME_DEPRECATED; typedef gpgme_key_sig_t GpgmeKeySig _GPGME_DEPRECATED; typedef gpgme_user_id_t GpgmeUserID _GPGME_DEPRECATED; typedef gpgme_key_t GpgmeKey _GPGME_DEPRECATED; typedef gpgme_passphrase_cb_t GpgmePassphraseCb _GPGME_DEPRECATED; typedef gpgme_progress_cb_t GpgmeProgressCb _GPGME_DEPRECATED; typedef gpgme_io_cb_t GpgmeIOCb _GPGME_DEPRECATED; typedef gpgme_register_io_cb_t GpgmeRegisterIOCb _GPGME_DEPRECATED; typedef gpgme_remove_io_cb_t GpgmeRemoveIOCb _GPGME_DEPRECATED; typedef gpgme_event_io_t GpgmeEventIO _GPGME_DEPRECATED; typedef gpgme_event_io_cb_t GpgmeEventIOCb _GPGME_DEPRECATED; #define GpgmeIOCbs gpgme_io_cbs typedef gpgme_data_read_cb_t GpgmeDataReadCb _GPGME_DEPRECATED; typedef gpgme_data_write_cb_t GpgmeDataWriteCb _GPGME_DEPRECATED; typedef gpgme_data_seek_cb_t GpgmeDataSeekCb _GPGME_DEPRECATED; typedef gpgme_data_release_cb_t GpgmeDataReleaseCb _GPGME_DEPRECATED; #define GpgmeDataCbs gpgme_data_cbs typedef gpgme_encrypt_result_t GpgmeEncryptResult _GPGME_DEPRECATED; typedef gpgme_sig_notation_t GpgmeSigNotation _GPGME_DEPRECATED; typedef gpgme_signature_t GpgmeSignature _GPGME_DEPRECATED; typedef gpgme_verify_result_t GpgmeVerifyResult _GPGME_DEPRECATED; typedef gpgme_import_status_t GpgmeImportStatus _GPGME_DEPRECATED; typedef gpgme_import_result_t GpgmeImportResult _GPGME_DEPRECATED; typedef gpgme_genkey_result_t GpgmeGenKeyResult _GPGME_DEPRECATED; typedef gpgme_trust_item_t GpgmeTrustItem _GPGME_DEPRECATED; typedef gpgme_status_code_t GpgmeStatusCode _GPGME_DEPRECATED; #ifdef __cplusplus } #endif #endif /* GPGME_H */ /* @emacs_local_vars_begin@ @emacs_local_vars_read_only@ @emacs_local_vars_end@ */ diff --git a/src/mkstatus b/src/mkstatus deleted file mode 100755 index 3fbfbf9e..00000000 --- a/src/mkstatus +++ /dev/null @@ -1,54 +0,0 @@ -#!/bin/sh -# mkstatus - Extract error strings from rungpg.h -# and create a lookup table -# Copyright (C) 2000 Werner Koch (dd9jn) -# Copyright (C) 2001 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 - -# resetting collate is important, so that the bsearch works properly -LC_ALL=C -LC_COLLATE=C -export LC_ALL LC_COLLATE - -cat < +#endif + +#include + +#include "util.h" + +struct status_table_s { + const char *name; + gpgme_status_code_t code; +}; + + +/* Lexicographically sorted ('_' comes after any letter). You can use + the Emacs command M-x sort-lines. But don't sweat it, the table is + sorted at start up, too. */ +static struct status_table_s status_table[] = +{ + { "ALREADY_SIGNED", GPGME_STATUS_ALREADY_SIGNED }, + { "BACKUP_KEY_CREATED", GPGME_STATUS_BACKUP_KEY_CREATED }, + { "BADARMOR", GPGME_STATUS_BADARMOR }, + { "BADMDC", GPGME_STATUS_BADMDC }, + { "BADSIG", GPGME_STATUS_BADSIG }, + { "BAD_PASSPHRASE", GPGME_STATUS_BAD_PASSPHRASE }, + { "BEGIN_DECRYPTION", GPGME_STATUS_BEGIN_DECRYPTION }, + { "BEGIN_ENCRYPTION", GPGME_STATUS_BEGIN_ENCRYPTION }, + { "BEGIN_STREAM", GPGME_STATUS_BEGIN_STREAM }, + { "CARDCTRL", GPGME_STATUS_CARDCTRL }, + { "DECRYPTION_FAILED", GPGME_STATUS_DECRYPTION_FAILED }, + { "DECRYPTION_OKAY", GPGME_STATUS_DECRYPTION_OKAY }, + { "DELETE_PROBLEM", GPGME_STATUS_DELETE_PROBLEM }, + { "ENC_TO", GPGME_STATUS_ENC_TO }, + { "END_DECRYPTION", GPGME_STATUS_END_DECRYPTION }, + { "END_ENCRYPTION", GPGME_STATUS_END_ENCRYPTION }, + { "END_STREAM", GPGME_STATUS_END_STREAM }, + { "ENTER", GPGME_STATUS_ENTER }, + { "ERRMDC", GPGME_STATUS_ERRMDC }, + { "ERROR", GPGME_STATUS_ERROR }, + { "ERRSIG", GPGME_STATUS_ERRSIG }, + { "EXPKEYSIG", GPGME_STATUS_EXPKEYSIG }, + { "EXPSIG", GPGME_STATUS_EXPSIG }, + { "FILE_DONE", GPGME_STATUS_FILE_DONE }, + { "FILE_ERROR", GPGME_STATUS_FILE_ERROR }, + { "FILE_START", GPGME_STATUS_FILE_START }, + { "GET_BOOL", GPGME_STATUS_GET_BOOL }, + { "GET_HIDDEN", GPGME_STATUS_GET_HIDDEN }, + { "GET_LINE", GPGME_STATUS_GET_LINE }, + { "GOODMDC", GPGME_STATUS_GOODMDC }, + { "GOODSIG", GPGME_STATUS_GOODSIG }, + { "GOOD_PASSPHRASE", GPGME_STATUS_GOOD_PASSPHRASE }, + { "GOT_IT", GPGME_STATUS_GOT_IT }, + { "IMPORTED", GPGME_STATUS_IMPORTED }, + { "IMPORT_OK", GPGME_STATUS_IMPORT_OK }, + { "IMPORT_PROBLEM", GPGME_STATUS_IMPORT_PROBLEM }, + { "IMPORT_RES", GPGME_STATUS_IMPORT_RES }, + { "INV_RECP", GPGME_STATUS_INV_RECP }, + { "INV_SGNR", GPGME_STATUS_INV_SGNR }, + { "KEYEXPIRED", GPGME_STATUS_KEYEXPIRED }, + { "KEYREVOKED", GPGME_STATUS_KEYREVOKED }, + { "KEY_CREATED", GPGME_STATUS_KEY_CREATED }, + { "LEAVE", GPGME_STATUS_LEAVE }, + { "MISSING_PASSPHRASE", GPGME_STATUS_MISSING_PASSPHRASE }, + { "NEED_PASSPHRASE", GPGME_STATUS_NEED_PASSPHRASE }, + { "NEED_PASSPHRASE_PIN", GPGME_STATUS_NEED_PASSPHRASE_PIN }, + { "NEED_PASSPHRASE_SYM", GPGME_STATUS_NEED_PASSPHRASE_SYM }, + { "NEWSIG", GPGME_STATUS_NEWSIG }, + { "NODATA", GPGME_STATUS_NODATA }, + { "NOTATION_DATA", GPGME_STATUS_NOTATION_DATA }, + { "NOTATION_NAME", GPGME_STATUS_NOTATION_NAME }, + { "NO_PUBKEY", GPGME_STATUS_NO_PUBKEY }, + { "NO_RECP", GPGME_STATUS_NO_RECP }, + { "NO_SECKEY", GPGME_STATUS_NO_SECKEY }, + { "NO_SGNR", GPGME_STATUS_NO_SGNR }, + { "PKA_TRUST_BAD", GPGME_STATUS_PKA_TRUST_BAD }, + { "PKA_TRUST_GOOD", GPGME_STATUS_PKA_TRUST_GOOD }, + { "PLAINTEXT", GPGME_STATUS_PLAINTEXT }, + { "POLICY_URL", GPGME_STATUS_POLICY_URL }, + { "PROGRESS", GPGME_STATUS_PROGRESS }, + { "REVKEYSIG", GPGME_STATUS_REVKEYSIG }, + { "RSA_OR_IDEA", GPGME_STATUS_RSA_OR_IDEA }, + { "SC_OP_FAILURE", GPGME_STATUS_SC_OP_FAILURE }, + { "SC_OP_SUCCESS", GPGME_STATUS_SC_OP_SUCCESS }, + { "SESSION_KEY", GPGME_STATUS_SESSION_KEY }, + { "SHM_GET", GPGME_STATUS_SHM_GET }, + { "SHM_GET_BOOL", GPGME_STATUS_SHM_GET_BOOL }, + { "SHM_GET_HIDDEN", GPGME_STATUS_SHM_GET_HIDDEN }, + { "SHM_INFO", GPGME_STATUS_SHM_INFO }, + { "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED }, + { "SIG_CREATED", GPGME_STATUS_SIG_CREATED }, + { "SIG_ID", GPGME_STATUS_SIG_ID }, + { "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET }, + { "SUCCESS", GPGME_STATUS_SUCCESS }, + { "TRUNCATED", GPGME_STATUS_TRUNCATED }, + { "TRUST_FULLY", GPGME_STATUS_TRUST_FULLY }, + { "TRUST_MARGINAL", GPGME_STATUS_TRUST_MARGINAL }, + { "TRUST_NEVER", GPGME_STATUS_TRUST_NEVER }, + { "TRUST_ULTIMATE", GPGME_STATUS_TRUST_ULTIMATE }, + { "TRUST_UNDEFINED", GPGME_STATUS_TRUST_UNDEFINED }, + { "UNEXPECTED", GPGME_STATUS_UNEXPECTED }, + { "USERID_HINT", GPGME_STATUS_USERID_HINT }, + { "VALIDSIG", GPGME_STATUS_VALIDSIG }, + { "ABORT", GPGME_STATUS_ABORT }, + {NULL, 0} +}; + + +static int +status_cmp (const void *ap, const void *bp) +{ + const struct status_table_s *a = ap; + const struct status_table_s *b = bp; + + return strcmp (a->name, b->name); +} + + +void +_gpgme_status_init (void) +{ + qsort (status_table, + DIM(status_table) - 1, sizeof (status_table[0]), + status_cmp); +} + + +gpgme_status_code_t +_gpgme_parse_status (const char *name) +{ + struct status_table_s t, *r; + t.name = name; + r = bsearch (&t, status_table, DIM(status_table) - 1, + sizeof t, status_cmp); + return r ? r->code : -1; +} diff --git a/src/util.h b/src/util.h index 27620aa7..cf18099a 100644 --- a/src/util.h +++ b/src/util.h @@ -1,169 +1,175 @@ /* util.h Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005 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. */ #ifndef UTIL_H #define UTIL_H #ifdef HAVE_W32_SYSTEM # ifdef HAVE_W32CE_SYSTEM # include "w32-ce.h" # else # include "windows.h" # endif #endif /* For pid_t. */ #ifdef HAVE_SYS_TYPES_H # include #endif /* We must see the symbol ttyname_r before a redefinition. */ #ifdef HAVE_UNISTD_H # include #endif #include "gpgme.h" #define DIM(v) (sizeof(v)/sizeof((v)[0])) /*-- {posix,w32}-util.c --*/ const char *_gpgme_get_gpg_path (void); const char *_gpgme_get_gpgsm_path (void); const char *_gpgme_get_gpgconf_path (void); const char *_gpgme_get_g13_path (void); const char *_gpgme_get_uiserver_socket_path (void); int _gpgme_get_conf_int (const char *key, int *value); void _gpgme_allow_set_foreground_window (pid_t pid); /*-- dirinfo.c --*/ const char *_gpgme_get_default_homedir (void); const char *_gpgme_get_default_agent_socket (void); /*-- replacement functions in .c --*/ #ifdef HAVE_CONFIG_H #ifndef HAVE_STPCPY static _GPGME_INLINE char * _gpgme_stpcpy (char *a, const char *b) { while (*b) *a++ = *b++; *a = 0; return a; } #define stpcpy(a,b) _gpgme_stpcpy ((a), (b)) #endif /*!HAVE_STPCPY*/ #if !HAVE_VASPRINTF #include int vasprintf (char **result, const char *format, va_list args); int asprintf (char **result, const char *format, ...); #endif #if REPLACE_TTYNAME_R int _gpgme_ttyname_r (int fd, char *buf, size_t buflen); #undef ttyname_r #define ttyname_r(a,b,c) _gpgme_ttyname_r ((a), (b), (c)) #endif #endif /*HAVE_CONFIG_H*/ /*-- conversion.c --*/ /* Convert two hexadecimal digits from STR to the value they represent. Returns -1 if one of the characters is not a hexadecimal digit. */ int _gpgme_hextobyte (const char *str); /* Decode the C formatted string SRC and store the result in the buffer *DESTP which is LEN bytes long. If LEN is zero, then a large enough buffer is allocated with malloc and *DESTP is set to the result. Currently, LEN is only used to specify if allocation is desired or not, the caller is expected to make sure that *DESTP is large enough if LEN is not zero. */ gpgme_error_t _gpgme_decode_c_string (const char *src, char **destp, size_t len); /* Decode the percent escaped string SRC and store the result in the buffer *DESTP which is LEN bytes long. If LEN is zero, then a large enough buffer is allocated with malloc and *DESTP is set to the result. Currently, LEN is only used to specify if allocation is desired or not, the caller is expected to make sure that *DESTP is large enough if LEN is not zero. If BINARY is 1, then '\0' characters are allowed in the output. */ gpgme_error_t _gpgme_decode_percent_string (const char *src, char **destp, size_t len, int binary); gpgme_error_t _gpgme_encode_percent_string (const char *src, char **destp, size_t len); /* Parse the string TIMESTAMP into a time_t. The string may either be seconds since Epoch or in the ISO 8601 format like "20390815T143012". Returns 0 for an empty string or seconds since Epoch. Leading spaces are skipped. If ENDP is not NULL, it will point to the next non-parsed character in TIMESTRING. */ time_t _gpgme_parse_timestamp (const char *timestamp, char **endp); gpgme_error_t _gpgme_map_gnupg_error (char *err); /* Retrieve the environment variable NAME and return a copy of it in a malloc()'ed buffer in *VALUE. If the environment variable is not set, return NULL in *VALUE. */ gpgme_error_t _gpgme_getenv (const char *name, char **value); + +/*-- status-table.c --*/ +/* Convert a status string to a status code. */ +void _gpgme_status_init (void); +gpgme_status_code_t _gpgme_parse_status (const char *name); + #ifdef HAVE_W32_SYSTEM int _gpgme_mkstemp (int *fd, char **name); const char *_gpgme_get_w32spawn_path (void); #endif /*HAVE_W32_SYSTEM*/ #ifdef HAVE_W32CE_SYSTEM char *_gpgme_w32ce_get_debug_envvar (void); #endif /*HAVE_W32CE_SYSTEM*/ /*-- Error codes not yet available in current gpg-error.h. --*/ #ifndef GPG_ERR_UNFINISHED #define GPG_ERR_UNFINISHED 199 #endif #ifndef GPG_ERR_NOT_OPERATIONAL #define GPG_ERR_NOT_OPERATIONAL 176 #endif #ifndef GPG_ERR_MISSING_ISSUER_CERT #define GPG_ERR_MISSING_ISSUER_CERT 185 #endif #ifdef ENABLE_ASSUAN #include /* System hooks for assuan integration. */ extern struct assuan_system_hooks _gpgme_assuan_system_hooks; extern struct assuan_malloc_hooks _gpgme_assuan_malloc_hooks; int _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook, unsigned int cat, const char *msg); #endif #endif /* UTIL_H */ diff --git a/src/version.c b/src/version.c index e02037e9..3aef4045 100644 --- a/src/version.c +++ b/src/version.c @@ -1,351 +1,353 @@ /* version.c - Version check routines. Copyright (C) 2000 Werner Koch (dd9jn) Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2008 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 #endif #include #include #include #include #ifdef HAVE_W32_SYSTEM #include #endif #include "gpgme.h" #include "priv-io.h" #include "debug.h" #include "context.h" -/* For _gpgme_sema_subsystem_init (). */ +/* For _gpgme_sema_subsystem_init and _gpgme_status_init. */ #include "sema.h" +#include "util.h" #ifdef HAVE_ASSUAN_H #include "assuan.h" #endif #ifdef HAVE_W32_SYSTEM #include "windows.h" #endif /* We implement this function, so we have to disable the overriding macro. */ #undef gpgme_check_version /* Bootstrap the subsystems needed for concurrent operation. This must be done once at startup. We can not guarantee this using a lock, though, because the semaphore subsystem needs to be initialized itself before it can be used. So we expect that the user performs the necessary synchronization. */ static void do_subsystem_inits (void) { static int done = 0; if (done) return; #ifdef HAVE_W32_SYSTEM /* We need to make sure that the sockets are initialized. */ { WSADATA wsadat; WSAStartup (0x202, &wsadat); } #endif _gpgme_sema_subsystem_init (); _gpgme_debug_subsystem_init (); _gpgme_io_subsystem_init (); + _gpgme_status_init (); done = 1; } /* Read the next number in the version string STR and return it in *NUMBER. Return a pointer to the tail of STR after parsing, or *NULL if the version string was invalid. */ static const char * parse_version_number (const char *str, int *number) { #define MAXVAL ((INT_MAX - 10) / 10) int val = 0; /* Leading zeros are not allowed. */ if (*str == '0' && isdigit(str[1])) return NULL; while (isdigit (*str) && val <= MAXVAL) { val *= 10; val += *(str++) - '0'; } *number = val; return val > MAXVAL ? NULL : str; } /* Parse the version string STR in the format MAJOR.MINOR.MICRO (for example, 9.3.2) and return the components in MAJOR, MINOR and MICRO as integers. The function returns the tail of the string that follows the version number. This might be te empty string if there is nothing following the version number, or a patchlevel. The function returns NULL if the version string is not valid. */ static const char * parse_version_string (const char *str, int *major, int *minor, int *micro) { str = parse_version_number (str, major); if (!str || *str != '.') return NULL; str++; str = parse_version_number (str, minor); if (!str || *str != '.') return NULL; str++; str = parse_version_number (str, micro); if (!str) return NULL; /* A patchlevel might follow. */ return str; } /* Return true if MY_VERSION is at least REQ_VERSION, and false otherwise. */ int _gpgme_compare_versions (const char *my_version, const char *rq_version) { int my_major, my_minor, my_micro; int rq_major, rq_minor, rq_micro; const char *my_plvl, *rq_plvl; if (!rq_version) return 1; if (!my_version) return 0; my_plvl = parse_version_string (my_version, &my_major, &my_minor, &my_micro); if (!my_plvl) return 0; rq_plvl = parse_version_string (rq_version, &rq_major, &rq_minor, &rq_micro); if (!rq_plvl) return 0; if (my_major > rq_major || (my_major == rq_major && my_minor > rq_minor) || (my_major == rq_major && my_minor == rq_minor && my_micro > rq_micro) || (my_major == rq_major && my_minor == rq_minor && my_micro == rq_micro && strcmp (my_plvl, rq_plvl) >= 0)) return 1; return 0; } /* Check that the the version of the library is at minimum the requested one and return the version string; return NULL if the condition is not met. If a NULL is passed to this function, no check is done and the version string is simply returned. This function must be run once at startup, as it also initializes some subsystems. Its invocation must be synchronized against calling any of the other functions in a multi-threaded environments. */ const char * gpgme_check_version (const char *req_version) { char *result; do_subsystem_inits (); /* Catch-22: We need to get at least the debug subsystem ready before using the trace facility. If we won't the trace would automagically initialize the debug system with out the locks being initialized and missing the assuan log level setting. */ TRACE2 (DEBUG_INIT, "gpgme_check_version", 0, "req_version=%s, VERSION=%s", req_version? req_version:"(null)", VERSION); result = _gpgme_compare_versions (VERSION, req_version) ? VERSION : NULL; if (result != NULL) _gpgme_selftest = 0; return result; } /* Check the version and also at runtime if the struct layout of the library matches the one of the user. This is particular useful for Windows targets (-mms-bitfields). */ const char * gpgme_check_version_internal (const char *req_version, size_t offset_sig_validity) { const char *result; result = gpgme_check_version (req_version); if (result == NULL) return result; /* Catch-22, see above. */ TRACE2 (DEBUG_INIT, "gpgme_check_version_internal", 0, "req_version=%s, offset_sig_validity=%i", req_version ? req_version : "(null)", offset_sig_validity); if (offset_sig_validity != offsetof (struct _gpgme_signature, validity)) { TRACE1 (DEBUG_INIT, "gpgme_check_version_internal", 0, "offset_sig_validity mismatch: expected %i", offsetof (struct _gpgme_signature, validity)); _gpgme_selftest = GPG_ERR_SELFTEST_FAILED; } return result; } #define LINELENGTH 80 /* Extract the version string of a program from STRING. The version number is expected to be in GNU style format: foo 1.2.3 foo (bar system) 1.2.3 foo 1.2.3 cruft foo (bar system) 1.2.3 cruft. Spaces and tabs are skipped and used as delimiters, a term in (nested) parenthesis before the version string is skipped, the version string may consist of any non-space and non-tab characters but needs to bstart with a digit. */ static const char * extract_version_string (const char *string, size_t *r_len) { const char *s; int count, len; for (s=string; *s; s++) if (*s == ' ' || *s == '\t') break; while (*s == ' ' || *s == '\t') s++; if (*s == '(') { for (count=1, s++; count && *s; s++) if (*s == '(') count++; else if (*s == ')') count--; } /* For robustness we look for a digit. */ while ( *s && !(*s >= '0' && *s <= '9') ) s++; if (*s >= '0' && *s <= '9') { for (len=0; s[len]; len++) if (s[len] == ' ' || s[len] == '\t') break; } else len = 0; *r_len = len; return s; } /* Retrieve the version number from the --version output of the program FILE_NAME. */ char * _gpgme_get_program_version (const char *const file_name) { char line[LINELENGTH] = ""; int linelen = 0; char *mark = NULL; int rp[2]; int nread; char *argv[] = {NULL /* file_name */, "--version", 0}; struct spawn_fd_item_s cfd[] = { {-1, 1 /* STDOUT_FILENO */, -1, 0}, {-1, -1} }; int status; if (!file_name) return NULL; argv[0] = (char *) file_name; if (_gpgme_io_pipe (rp, 1) < 0) return NULL; cfd[0].fd = rp[1]; status = _gpgme_io_spawn (file_name, argv, 0, cfd, NULL, NULL, NULL); if (status < 0) { _gpgme_io_close (rp[0]); _gpgme_io_close (rp[1]); return NULL; } do { nread = _gpgme_io_read (rp[0], &line[linelen], LINELENGTH - linelen - 1); if (nread > 0) { line[linelen + nread] = '\0'; mark = strchr (&line[linelen], '\n'); if (mark) { if (mark > &line[0] && *mark == '\r') mark--; *mark = '\0'; break; } linelen += nread; } } while (nread > 0 && linelen < LINELENGTH - 1); _gpgme_io_close (rp[0]); if (mark) { size_t len; const char *s; s = extract_version_string (line, &len); if (!len) return NULL; mark = malloc (len + 1); if (!mark) return NULL; memcpy (mark, s, len); mark[len] = 0; return mark; } return NULL; }