aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/ChangeLog577
-rw-r--r--common/Makefile.am60
-rw-r--r--common/README11
-rw-r--r--common/asshelp.c169
-rw-r--r--common/asshelp.h38
-rw-r--r--common/b64enc.c214
-rw-r--r--common/dns-cert.c246
-rw-r--r--common/dns-cert.h28
-rw-r--r--common/dynload.h72
-rw-r--r--common/errors.h113
-rw-r--r--common/estream.c2615
-rw-r--r--common/estream.h203
-rw-r--r--common/exechelp.c483
-rw-r--r--common/exechelp.h46
-rw-r--r--common/gettime.c305
-rw-r--r--common/homedir.c126
-rw-r--r--common/i18n.h48
-rw-r--r--common/iobuf.c2467
-rw-r--r--common/iobuf.h174
-rw-r--r--common/isascii.c30
-rw-r--r--common/maperror.c105
-rw-r--r--common/membuf.c95
-rw-r--r--common/membuf.h42
-rw-r--r--common/miscellaneous.c151
-rwxr-xr-xcommon/mkerrors73
-rwxr-xr-xcommon/mkerrtok68
-rw-r--r--common/pka.c252
-rw-r--r--common/pka.h27
-rw-r--r--common/sexp-parse.h100
-rw-r--r--common/sexputil.c145
-rw-r--r--common/signal.c259
-rw-r--r--common/simple-gettext.c438
-rw-r--r--common/simple-pwquery.c630
-rw-r--r--common/simple-pwquery.h76
-rw-r--r--common/sysutils.c231
-rw-r--r--common/sysutils.h47
-rw-r--r--common/ttyio.c599
-rw-r--r--common/ttyio.h61
-rw-r--r--common/util.h209
-rw-r--r--common/vasprintf.c169
-rw-r--r--common/w32reg.c174
-rw-r--r--common/xasprintf.c63
-rw-r--r--common/xreadline.c117
-rw-r--r--common/yesno.c139
44 files changed, 12295 insertions, 0 deletions
diff --git a/common/ChangeLog b/common/ChangeLog
new file mode 100644
index 000000000..36a733a7f
--- /dev/null
+++ b/common/ChangeLog
@@ -0,0 +1,577 @@
+2006-05-23 Werner Koch <[email protected]>
+
+ * gettime.c (isotimestamp): New.
+
+ * ttyio.c (tty_get_ttyname): Posixly correct usage of ctermid.
+
+ * dns-cert.c: New. Taken from 1.4.3's util/cert.c.
+ * dns-cert.h: New.
+
+2006-05-22 Werner Koch <[email protected]>
+
+ * pka.c: New. Taked from 1.4.3.
+ * pka.h: New.
+ * Makefile.am: Added pka.
+
+2006-05-19 Werner Koch <[email protected]>
+
+ * yesno.c (answer_is_yes_no_default, answer_is_yes_no_quit):
+ Updated from 1.4.3.
+ (answer_is_okay_cancel): new. From 1.4.3.
+
+ * miscellaneous.c (match_multistr): New. Taken from 1.4.3.
+
+ * ttyio.c (tty_enable_completion, tty_disable_completion): New
+ dummy functions.
+ * ttyio.h: Add prototypes and stubs.
+
+2006-04-19 Werner Koch <[email protected]>
+
+ * iobuf.c (iobuf_get_fd): New. Taken from 1.4.3.
+ (iobuf_is_pipe_filename): New.
+ (pop_filter): Made static.
+ (iobuf_skip_rest): New. Orginal patch by Florian
+ Weimer. Added new argument PARTIAL.
+ (block_filter): Remove the old gpg indeterminate length mode.
+ (block_filter): Properly handle a partial body stream
+ that ends with a 5-byte length that happens to be zero.
+ (iobuf_set_block_mode, iobuf_in_block_mode): Removed as
+ superfluous.
+ (iobuf_get_filelength): New arg OVERFLOW.
+ (iobuf_get_filelength) [W32]: Use GetFileSizeEx if available
+ * miscellaneous.c (is_file_compressed): Take care of OVERFLOW.
+
+2006-04-18 Werner Koch <[email protected]>
+
+ * homedir.c (w32_shgetfolderpath): New. Taken from gpg 1.4.3.
+ (default_homedir): Use it.
+
+2005-10-08 Marcus Brinkmann <[email protected]>
+
+ * signal.c (get_signal_name): Check value of HAVE_DECL_SYS_SIGLIST
+ instead of just if it is defined.
+
+2005-09-28 Marcus Brinkmann <[email protected]>
+
+ * Makefile.am (AM_CFLAGS): Add $(LIBASSUAN_CFLAGS).
+
+2005-07-04 Marcus Brinkmann <[email protected]>
+
+ * simple-pwquery.h (simple_pwclear): New prototype.
+ * simple-pwquery.c (simple_pwclear): New function.
+
+2005-06-15 Werner Koch <[email protected]>
+
+ * miscellaneous.c (make_printable_string): Made P a void*.
+
+ * sexputil.c (keygrip_from_canon_sexp, cmp_simple_canon_sexp):
+ Fixed signed/unsigned pointer mismatch.
+ (make_simple_sexp_from_hexstr): Ditto. This is all too ugly; I
+ wonder why gcc-4's default is to warn about them and forcing us to
+ use cast the warning away.
+ * iobuf.c (block_filter): Ditto.
+ (iobuf_flush): Ditto.
+ (iobuf_read_line): Ditto.
+ (iobuf_read): Make BUFFER a void *.
+ (iobuf_write): Make BUFFER a const void *.
+ * ttyio.c (tty_print_utf8_string2): Ditto.
+ * estream.c (estream_cookie_mem): Make MEMORY unsigned char*.
+ (es_write): Make BUFFER a void *.
+ (es_writen): Ditto.
+ (es_func_fd_read, es_func_fd_write, es_func_mem_read)
+ (es_func_mem_write): Ditto.
+ (es_read, es_readn): Ditto.
+ (es_func_mem_write): Made MEMORY_NEW an unsigned char *.
+ * estream.h (es_cookie_read_function_t)
+ (es_cookie_write_function_t): Changed buffer arg to void*.
+
+2005-06-03 Werner Koch <[email protected]>
+
+ * estream.c: Use HAVE_CONFIG_H and not USE_CONFIG_H!
+ (es_func_fd_read, es_func_fd_write): Protect against EINTR.
+
+2005-06-01 Werner Koch <[email protected]>
+
+ * Makefile.am (AM_CPPFLAGS): Added.
+
+ * util.h: Add some includes for gnulib.
+ (ttyname, isascii): Define them inline.
+ * fseeko.c, ftello.c: Removed.
+ * strsep.c, mkdtemp.c: Removed.
+ * ttyname.c, isascii.c: Removed.
+
+2005-05-31 Werner Koch <[email protected]>
+
+ * dynload.h: s/__inline__/inline/.
+
+2005-05-13 Werner Koch <[email protected]>
+
+ * signal.c (got_fatal_signal): Print the signal number if we can't
+ get a name for it.
+ (get_signal_name): Return NULL if no name is available. Fixed
+ conditional for sys_siglist to the correct one.
+
+2005-04-17 Werner Koch <[email protected]>
+
+ * sexputil.c (cmp_simple_canon_sexp): New.
+ (make_simple_sexp_from_hexstr): New.
+
+2005-04-07 Werner Koch <[email protected]>
+
+ * sexputil.c: New.
+
+2005-04-11 Marcus Brinkmann <[email protected]>
+
+ * simple-pwquery.c (simple_pwquery): Use spwq_secure_free.
+
+2005-03-03 Werner Koch <[email protected]>
+
+ * Makefile.am (AM_CFLAGS): Added PTH_CFLAGS. Noted by Kazu Yamamoto.
+
+2005-02-25 Werner Koch <[email protected]>
+
+ * xasprintf.c (xtryasprintf): New.
+
+2005-01-26 Moritz Schulte <[email protected]>
+
+ * Makefile.am (libcommon_a_SOURCES): New source files: estream.c,
+ estream.h.
+ * estream.c, estream.h: New files.
+
+2005-01-03 Werner Koch <[email protected]>
+
+ * asshelp.c (send_pinentry_environment): Fixed changed from
+ 2004-12-18; cut+paste error for lc-messages.
+
+2004-12-21 Werner Koch <[email protected]>
+
+ * simple-pwquery.c (agent_open) [W32]: Implement for W32.
+ (readline) [W32]: Use recv instead of read.
+ (writen) [W32]: Use send instead of write.
+ (my_stpcpy): Define a stpcpy replacement so that this file
+ continues to be self-contained.
+ (agent_send_all_options) [W32]: Don't call ttyname.
+
+2004-12-21 Marcus Brinkmann <[email protected]>
+
+ * simple-pwquery.h (simple_query): Add prototype.
+ * simple-pwquery.c (simple_query): New function.
+
+2004-12-21 Werner Koch <[email protected]>
+
+ * signal.c (got_fatal_signal, got_usr_signal)
+ (got_fatal_signal) [DOSISH]: Don't build.
+ * simple-gettext.c: Include sysutils.h
+
+ * homedir.c: New. Use CSIDL_APPDATA for W32 as the default home
+ directory.
+ * Makefile.am (libcommon_a_SOURCES): Add it.
+ (EXTRA_DIST): Removed mkerror and mkerrtok.
+
+2004-12-20 Werner Koch <[email protected]>
+
+ * sysutils.h [W32]: Define sleep.
+ * util.h: Add prototype for mkdtemp.
+
+ * membuf.c (put_membuf): Wipe out buffer after a failed realloc.
+
+2004-12-19 Werner Koch <[email protected]>
+
+ * maperror.c (map_assuan_err_with_source): Oops, args were swapped.
+
+2004-12-18 Werner Koch <[email protected]>
+
+ * maperror.c (map_assuan_err): Renamed to ..
+ (map_assuan_err_with_source): .. this and add arg SOURCE.c
+ * asshelp.c (send_pinentry_environment, send_one_option): Add arg
+ ERRSOURCE.
+
+2004-12-15 Werner Koch <[email protected]>
+
+ * sysutils.h [W32]: Prototypes for registry functions.
+ * w32reg.c: Include sysutils.h
+
+ * simple-pwquery.c [W32]: Dummy code to allow a build.
+
+ * exechelp.c [W32]: Implemented for W32 .
+
+ * ttyname.c: New.
+
+ * asshelp.c (send_one_option): New.
+ (send_pinentry_environment): Cleaned up and made sure that empty
+ values are not send.
+
+2004-12-07 Werner Koch <[email protected]>
+
+ * asshelp.c (send_pinentry_environment) [W32]: Do not use ttyname.
+
+2004-12-06 Werner Koch <[email protected]>
+
+ * exechelp.h, exechelp.c: New. Based on code from ../sm/import.c.
+
+2004-12-03 Werner Koch <[email protected]>
+
+ * strsep.c: Fixed copyright comments.
+
+2004-11-26 Werner Koch <[email protected]>
+
+ * simple-gettext.c: New taken from gnupg 1.3.x
+
+ * simple-pwquery.c [_WIN32]: Include winsock2.h.
+ (agent_open): Disable it until we have our AF_UNIX implementation
+ ready.
+ * fseeko.c, ftello.c: Include sys/types for the sake of W32.
+
+2004-11-23 Werner Koch <[email protected]>
+
+ * b64enc.c: Include stdio.h and string.h
+
+2004-08-18 Werner Koch <[email protected]>
+
+ * simple-pwquery.c (simple_pwquery): Handle gpg-error style return
+ code for canceled.
+
+2004-07-20 Werner Koch <[email protected]>
+
+ * maperror.c: Removed header ksba.h. Not required anymore.
+
+2004-06-14 Werner Koch <[email protected]>
+
+ * xreadline.c: New. Based on the iobuf_read_line function.
+
+2004-05-12 Werner Koch <[email protected]>
+
+ * util.h (xtrycalloc_secure,xtrymalloc_secure): New.
+
+2004-05-11 Werner Koch <[email protected]>
+
+ * sysutils.c (disable_core_dumps): Only set the current limit.
+ (enable_core_dumps): New.
+
+2004-04-13 Werner Koch <[email protected]>
+
+ * simple-pwquery.c (copy_and_escape): Relaxed quoting.
+
+2004-04-05 Werner Koch <[email protected]>
+
+ * errors.h (STATUS_NEWSIG): New.
+
+2004-03-11 Werner Koch <[email protected]>
+
+ * dynload.h [__MINGW32__]: Define RTLD_LAZY.
+
+2004-03-09 Werner Koch <[email protected]>
+
+ * maperror.c (map_assuan_err): Map the Locale_Problem item.
+
+2004-03-03 Werner Koch <[email protected]>
+
+ * asshelp.c, asshelp.h: New.
+ (send_pinentry_environment): New. Code taken from ../sm/call-agent.c.
+
+2004-02-19 Werner Koch <[email protected]>
+
+ * simple-pwquery.c (agent_open): Don't mangle INFOSTR.
+
+2004-02-17 Werner Koch <[email protected]>
+
+ * simple-pwquery.c (agent_open): Ignore an empty GPG_AGENT_INFO.
+
+ * errors.h: Added STATUS_IMPORT_OK.
+
+2004-02-10 Werner Koch <[email protected]>
+
+ * b64enc.c: New. Based on code from ../sm/base64.c.
+
+2004-01-30 Marcus Brinkmann <[email protected]>
+
+ * Makefile.am (libcommon_a_SOURCES): Add xasprintf.c.
+ * miscellaneous.c (xasprintf): Moved to ...
+ * xasprintf (xasprintf): ... here. New file.
+ This allows to use xasprintf without sucking in gpg-error.
+
+2004-01-27 Werner Koch <[email protected]>
+
+ * sexp-parse.h: New; moved from../agent.
+
+ * util.h (xtoi_4): New.
+
+2003-12-23 Werner Koch <[email protected]>
+
+ * maperror.c (map_assuan_err): Prepared for a new error code.
+
+2003-12-17 Werner Koch <[email protected]>
+
+ * gettime.c (asctimestamp): Add a note on a non-avoidable gcc warning.
+
+ * util.h [!HAVE_VASPRINTF]: Add printf format attribute to the
+ replacement function.
+
+ * miscellaneous.c (xasprintf): New.
+
+2003-11-14 Werner Koch <[email protected]>
+
+ * mkdtemp.c (mkdtemp): Use gcry_create_nonce.
+
+ * cryptmiss.c: Removed.
+
+2003-11-13 Werner Koch <[email protected]>
+
+ * util.h (vasprintf): Also fixed the prototype.
+
+ * vasprintf.c (vasprintf): ARGS should not be a pointer. Fixed
+ segv on Solaris. Reported by Andrew J. Schorr.
+
+2003-11-12 Werner Koch <[email protected]>
+
+ * maperror.c (map_ksba_err, map_gcry_err, map_kbx_err): Removed.
+
+2003-10-31 Werner Koch <[email protected]>
+
+ * util.h (gnupg_isotime_t): New.
+ (gnupg_copy_time): New.
+
+ * gettime.c (gnupg_get_isotime): New.
+
+2003-09-23 Werner Koch <[email protected]>
+
+ * iobuf.c (check_special_filename): Replaced is isdigit by digitp
+ to avoid passing negative values and potential locale problems.
+ Problem noted by Christian Biere.
+
+ * util.h (ascii_isspace): New.
+
+2003-09-18 Werner Koch <[email protected]>
+
+ * ttyio.c (tty_fprintf): New.
+ (tty_print_string, tty_print_utf8_string2)
+ (tty_print_utf8_string): Made P argument const byte*.
+
+2003-08-20 Marcus Brinkmann <[email protected]>
+
+ * maperror.c (map_ksba_err): Map -1. Use gpg_err_make to set
+ the error source.
+
+2003-08-14 Timo Schulz <[email protected]>
+
+ * dynload.h. New. W32 wrapper around the dynload mechanism.
+
+2003-07-15 Werner Koch <[email protected]>
+
+ * simple-pwquery.c, simple-pwquery.h: New; moved from ../agent.
+ * Makefile.am (libsimple_pwquery_a_LIBADD): New.
+
+2003-06-25 Werner Koch <[email protected]>
+
+ * maperror.c (map_to_assuan_status): Directly map 0 to 0.
+
+2003-06-17 Werner Koch <[email protected]>
+
+ * gettime.c (scan_isodatestr,add_days_to_timestamp,strtimevalue)
+ (strtimestamp,asctimestamp): New. Code taken from gnupg 1.3.2
+ mischelp.c.
+
+ * yesno.c: New. Code taken from gnupg 1.3.2 mischelp.c
+
+ * miscellaneous.c: New.
+
+ * util.h: Include utf8conf.h
+
+2003-06-16 Werner Koch <[email protected]>
+
+ * gettime.c (make_timestamp): New.
+
+ * ttyio.c: New. Taken from gnupg 1.2.
+ * ttyio.h: Move from ../include.
+
+2003-06-13 Werner Koch <[email protected]>
+
+ * util.h (seterr): Removed macro.
+ (xmalloc_secure,xcalloc_secure): New.
+
+2003-06-11 Werner Koch <[email protected]>
+
+ * iobuf.c (iobuf_writebyte,iobuf_write): Return error code from
+ iobuf_flush.
+ (iobuf_writestr): Ditto.
+
+2003-06-10 Werner Koch <[email protected]>
+
+ * iobuf.c, iobuf.h: New. Taken from current gnupg 1.3 CVS. Run
+ indent on it and adjusted error handling to libgpg-error style.
+ Replaced IOBUF by iobuf_t. Renamed malloc functions.
+
+2003-06-04 Werner Koch <[email protected]>
+
+ * errors.h: Removed all error codes. We keep the status codes for
+ now.
+ * Makefile.am: Do not create errors.c anymore; remove it from the
+ sources.
+
+ * maperror.c: Don't include error.h. Change all error codes to
+ libgpg-error style.
+ (map_assuan_err): Changed to new Assuan error code convention.
+ (map_to_assuan_status): Likewise.
+ (map_gcry_err,map_kbx_err): Not needed. For now dummy functions.
+
+ * membuf.c, membuf.h: New. Code taken from ../sm/call-agent.h.
+ * Makefile.am: Added above.
+
+2003-04-29 Werner Koch <[email protected]>
+
+ * util.h (fopencokokie): Removed prototype and struct.
+
+ * fopencookie.c: Removed.
+
+ * maperror.c: Use system assuan.h
+
+2002-10-31 Neal H. Walfield <[email protected]>
+
+ * isascii.c: New file.
+ * putc_unlocked.c: Likewise.
+
+2002-10-28 Neal H. Walfield <[email protected]>
+
+ * signal.c (caught_fatal_sig): Remove superfluous zero
+ initializer.
+ (caught_sigusr1): Likewise.
+
+2002-09-04 Neal H. Walfield <[email protected]>
+
+ * vasprintf.c (vasprintf) [va_copy]: Use va_copy.
+ [!va_copy && __va_copy]: Use __va_copy.
+ [!va_copy && !__va_copy]: Only now fall back to using memcpy.
+
+2002-08-21 Werner Koch <[email protected]>
+
+ * errors.h: Added STATUS_IMPORT_PROBLEM.
+
+2002-08-20 Werner Koch <[email protected]>
+
+ * vasprintf.c: Hack to handle NULL for %s.
+
+2002-08-09 Werner Koch <[email protected]>
+
+ * signal.c: New. Taken from GnuPG 1.1.91.
+
+2002-07-23 Werner Koch <[email protected]>
+
+ * util.h (_IO_cookie_io_functions_t): Fixed typo. Noted by
+ Richard Lefebvre.
+
+2002-07-22 Werner Koch <[email protected]>
+
+ * fseeko.c, ftello.c: New.
+
+2002-06-28 Werner Koch <[email protected]>
+
+ * maperror.c (map_to_assuan_status): Map more errorcodes to Bad
+ Certificate.
+
+2002-06-26 Werner Koch <[email protected]>
+
+ * maperror.c (map_to_assuan_status): Map EOF to No_Data_Available.
+
+2002-06-10 Werner Koch <[email protected]>
+
+ * errors.h (gnupg_error_token): Add new prototype.
+ (STATUS_ERROR): New.
+
+ * mkerrtok: New.
+ * Makefile.am: Use it to create the new error token function.
+
+2002-06-04 Werner Koch <[email protected]>
+
+ * maperror.c (map_to_assuan_status): Map Bad_CA_Certificate.
+
+2002-05-23 Werner Koch <[email protected]>
+
+ * no-pth.c, Makefile.am: Removed.
+
+2002-05-22 Werner Koch <[email protected]>
+
+ * mkdtemp.c: Replaced byte by unsigned char because it is no longer
+ defined in gcrypt.h.
+
+2002-05-21 Werner Koch <[email protected]>
+
+ * maperror.c (map_gcry_err): Add libgcrypt's new S-expression errors.
+ (map_ksba_err): Add a few mappings.
+
+2002-05-14 Werner Koch <[email protected]>
+
+ * gettime.c: New.
+
+2002-05-03 Werner Koch <[email protected]>
+
+ * errors.h: Added STARUS_EXPSIG and STATUS_EXPKEYSIG.
+
+2002-04-15 Werner Koch <[email protected]>
+
+ * cryptmiss.c: New.
+
+2002-02-14 Werner Koch <[email protected]>
+
+ * maperror.c: Add more assuan<->gnupg mappings.
+
+2002-02-12 Werner Koch <[email protected]>
+
+ * fopencookie.c: Dummy function.
+
+ * vasprintf.c: New. Taken from binutils-2.9.1 and dropped all non
+ ANSI-C stuff. Merged with asprintf version.
+
+ * no-pth.c: New.
+
+2002-01-23 Werner Koch <[email protected]>
+
+ * mkdtemp.c: Copied from gnupg-1.0.6c and changed to use libgcrypt.
+
+2002-01-19 Werner Koch <[email protected]>
+
+ * sysutils.c: New. This is the misc.c file from gnupg 1.0.6 with
+ the OpenPGP stuff removed.
+ * sysutils.h: New.
+
+2002-01-15 Werner Koch <[email protected]>
+
+ * maperror.c: Add mapping for Not_Trusted.
+
+2002-01-11 Werner Koch <[email protected]>
+
+ * maperror.c (map_assuan_err): Codes for CRL
+
+2002-01-08 Werner Koch <[email protected]>
+
+ * util.h (spacep): New.
+
+2002-01-02 Werner Koch <[email protected]>
+
+ * maperror.c (map_to_assuan_status): New. Merged from ../agent
+ and ../sm.
+
+2001-12-20 Werner Koch <[email protected]>
+
+ * maperror.c (map_gcry_err): Add some mappings.
+
+2001-12-18 Werner Koch <[email protected]>
+
+ * Makefile.am (AM_CPPFLAGS): Include flags for gcrypt and ksba
+
+2001-12-14 Werner Koch <[email protected]>
+
+ * util.h (digitp, hexdigitp): New ctype like macros.
+ (atoi_1,atoi_2,atoi_4,xtoi_1,xtoi_2): New.
+
+
+ Copyright 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+
+ 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.
+
+
diff --git a/common/Makefile.am b/common/Makefile.am
new file mode 100644
index 000000000..085440bb3
--- /dev/null
+++ b/common/Makefile.am
@@ -0,0 +1,60 @@
+# Makefile for common gnupg modules
+# Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+## Process this file with automake to produce Makefile.in
+
+noinst_LIBRARIES = libcommon.a libsimple-pwquery.a
+
+AM_CPPFLAGS = -I$(top_srcdir)/gl
+
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(KSBA_CFLAGS) \
+ $(PTH_CFLAGS)
+
+libcommon_a_SOURCES = \
+ util.h i18n.h \
+ errors.h \
+ sexp-parse.h \
+ sexputil.c \
+ maperror.c \
+ sysutils.c sysutils.h \
+ homedir.c \
+ gettime.c \
+ yesno.c \
+ b64enc.c \
+ miscellaneous.c \
+ xasprintf.c \
+ xreadline.c \
+ membuf.c membuf.h \
+ iobuf.c iobuf.h \
+ ttyio.c ttyio.h \
+ asshelp.c asshelp.h \
+ exechelp.c exechelp.h \
+ simple-gettext.c \
+ w32reg.c \
+ signal.c \
+ dynload.h \
+ estream.c estream.h \
+ dns-cert.c dns-cert.h \
+ pka.c pka.h
+
+
+libsimple_pwquery_a_SOURCES = \
+ simple-pwquery.c simple-pwquery.h asshelp.c asshelp.h
+
diff --git a/common/README b/common/README
new file mode 100644
index 000000000..a90224bab
--- /dev/null
+++ b/common/README
@@ -0,0 +1,11 @@
+Stuff used by several modules of GnuPG.
+
+These directories use it:
+
+gpg
+sm
+agent
+
+These directories don't use it:
+
+kbx \ No newline at end of file
diff --git a/common/asshelp.c b/common/asshelp.c
new file mode 100644
index 000000000..1da899522
--- /dev/null
+++ b/common/asshelp.c
@@ -0,0 +1,169 @@
+/* asshelp.c - Helper functions for Assuan
+ * Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "util.h"
+
+#include "asshelp.h"
+
+
+static gpg_error_t
+send_one_option (assuan_context_t ctx, gpg_err_source_t errsource,
+ const char *name, const char *value)
+{
+ gpg_error_t err;
+ char *optstr;
+
+ if (!value || !*value)
+ err = 0; /* Avoid sending empty strings. */
+ else if (asprintf (&optstr, "OPTION %s=%s", name, value ) < 0)
+ err = gpg_error_from_errno (errno);
+ else
+ {
+ assuan_error_t ae;
+
+ ae = assuan_transact (ctx, optstr, NULL, NULL, NULL, NULL, NULL, NULL);
+ err = ae? map_assuan_err_with_source (errsource, ae) : 0;
+ free (optstr);
+ }
+
+ return err;
+}
+
+
+/* Send the assuan commands pertaining to the pinenry environment. The
+ OPT_* arguments are optional and may be used to override the
+ defaults taken from the current locale. */
+gpg_error_t
+send_pinentry_environment (assuan_context_t ctx,
+ gpg_err_source_t errsource,
+ const char *opt_display,
+ const char *opt_ttyname,
+ const char *opt_ttytype,
+ const char *opt_lc_ctype,
+ const char *opt_lc_messages)
+{
+ gpg_error_t err = 0;
+ char *dft_display = NULL;
+ char *dft_ttyname = NULL;
+ char *dft_ttytype = NULL;
+ char *old_lc = NULL;
+ char *dft_lc = NULL;
+
+ /* Send the DISPLAY variable. */
+ dft_display = getenv ("DISPLAY");
+ if (opt_display || dft_display)
+ {
+ err = send_one_option (ctx, errsource, "display",
+ opt_display ? opt_display : dft_display);
+ if (err)
+ return err;
+ }
+
+ /* Send the name of the TTY. */
+ if (!opt_ttyname)
+ {
+ dft_ttyname = getenv ("GPG_TTY");
+ if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
+ dft_ttyname = ttyname (0);
+ }
+ if (opt_ttyname || dft_ttyname)
+ {
+ err = send_one_option (ctx, errsource, "ttyname",
+ opt_ttyname ? opt_ttyname : dft_ttyname);
+ if (err)
+ return err;
+ }
+
+ /* Send the type of the TTY. */
+ dft_ttytype = getenv ("TERM");
+ if (opt_ttytype || (dft_ttyname && dft_ttytype))
+ {
+ err = send_one_option (ctx, errsource, "ttytype",
+ opt_ttyname ? opt_ttytype : dft_ttytype);
+ if (err)
+ return err;
+ }
+
+ /* Send the value for LC_CTYPE. */
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ old_lc = setlocale (LC_CTYPE, NULL);
+ if (old_lc)
+ {
+ old_lc = strdup (old_lc);
+ if (!old_lc)
+ return gpg_error_from_errno (errno);
+ }
+ dft_lc = setlocale (LC_CTYPE, "");
+#endif
+ if (opt_lc_ctype || (dft_ttyname && dft_lc))
+ {
+ err = send_one_option (ctx, errsource, "lc-ctype",
+ opt_lc_ctype ? opt_lc_ctype : dft_lc);
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ if (old_lc)
+ {
+ setlocale (LC_CTYPE, old_lc);
+ free (old_lc);
+ }
+#endif
+ if (err)
+ return err;
+
+ /* Send the value for LC_MESSAGES. */
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ old_lc = setlocale (LC_MESSAGES, NULL);
+ if (old_lc)
+ {
+ old_lc = strdup (old_lc);
+ if (!old_lc)
+ return gpg_error_from_errno (errno);
+ }
+ dft_lc = setlocale (LC_MESSAGES, "");
+#endif
+ if (opt_lc_messages || (dft_ttyname && dft_lc))
+ {
+ err = send_one_option (ctx, errsource, "lc-messages",
+ opt_lc_messages ? opt_lc_messages : dft_lc);
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ if (old_lc)
+ {
+ setlocale (LC_MESSAGES, old_lc);
+ free (old_lc);
+ }
+#endif
+ if (err)
+ return err;
+
+ return 0;
+}
+
diff --git a/common/asshelp.h b/common/asshelp.h
new file mode 100644
index 000000000..9f4b5806b
--- /dev/null
+++ b/common/asshelp.h
@@ -0,0 +1,38 @@
+/* asshelp.h - Helper functions for Assuan
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_ASSHELP_H
+#define GNUPG_COMMON_ASSHELP_H
+
+#include <assuan.h>
+#include <gpg-error.h>
+
+gpg_error_t
+send_pinentry_environment (assuan_context_t ctx,
+ gpg_err_source_t errsource,
+ const char *opt_display,
+ const char *opt_ttyname,
+ const char *opt_ttytype,
+ const char *opt_lc_ctype,
+ const char *opt_lc_messages);
+
+
+#endif /*GNUPG_COMMON_ASSHELP_H*/
diff --git a/common/b64enc.c b/common/b64enc.c
new file mode 100644
index 000000000..bfc49deb6
--- /dev/null
+++ b/common/b64enc.c
@@ -0,0 +1,214 @@
+/* b64enc.c - Simple Base64 encoder.
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "i18n.h"
+#include "util.h"
+
+#define B64ENC_DID_HEADER 1
+#define B64ENC_DID_TRAILER 2
+#define B64ENC_NO_LINEFEEDS 16
+
+
+/* The base-64 character list */
+static unsigned char bintoasc[64] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+
+/* Prepare for base-64 writing to the stream FP. If TITLE is not NULL
+ and not an empty string, this string will be used as the title for
+ the armor lines, with TITLE being an empty string, we don't write
+ the header lines and furthermore even don't write any linefeeds.
+ With TITLE beeing NULL, we merely don't write header but make sure
+ that lines are not too long. Note, that we don't write any output
+ unless at least one byte get written using b64enc_write. */
+gpg_error_t
+b64enc_start (struct b64state *state, FILE *fp, const char *title)
+{
+ memset (state, 0, sizeof *state);
+ state->fp = fp;
+ if (title && !*title)
+ state->flags |= B64ENC_NO_LINEFEEDS;
+ else if (title)
+ {
+ state->title = xtrystrdup (title);
+ if (!state->title)
+ return gpg_error_from_errno (errno);
+ }
+ return 0;
+}
+
+
+/* Write NBYTES from BUFFER to the Base 64 stream identified by
+ STATE. With BUFFER and NBYTES being 0, merely do a fflush on the
+ stream. */
+gpg_error_t
+b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
+{
+ unsigned char radbuf[4];
+ int idx, quad_count;
+ const unsigned char *p;
+ FILE *fp = state->fp;
+
+
+ if (!nbytes)
+ {
+ if (buffer && fflush (fp))
+ goto write_error;
+ return 0;
+ }
+
+ if (!(state->flags & B64ENC_DID_HEADER))
+ {
+ if (state->title)
+ {
+ if ( fputs ("-----BEGIN ", fp) == EOF
+ || fputs (state->title, fp) == EOF
+ || fputs ("-----\n", fp) == EOF)
+ goto write_error;
+ }
+ state->flags |= B64ENC_DID_HEADER;
+ }
+
+ idx = state->idx;
+ quad_count = state->quad_count;
+ assert (idx < 4);
+ memcpy (radbuf, state->radbuf, idx);
+
+ for (p=buffer; nbytes; p++, nbytes--)
+ {
+ radbuf[idx++] = *p;
+ if (idx > 2)
+ {
+ char tmp[4];
+
+ tmp[0] = bintoasc[(*radbuf >> 2) & 077];
+ tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
+ tmp[2] = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
+ tmp[3] = bintoasc[radbuf[2]&077];
+ for (idx=0; idx < 4; idx++)
+ putc (tmp[idx], fp);
+ idx = 0;
+ if (ferror (fp))
+ goto write_error;
+ if (++quad_count >= (64/4))
+ {
+ quad_count = 0;
+ if (!(state->flags & B64ENC_NO_LINEFEEDS)
+ && fputs ("\n", fp) == EOF)
+ goto write_error;
+ }
+ }
+ }
+ memcpy (state->radbuf, radbuf, idx);
+ state->idx = idx;
+ state->quad_count = quad_count;
+ return 0;
+
+ write_error:
+ return gpg_error_from_errno (errno);
+}
+
+gpg_error_t
+b64enc_finish (struct b64state *state)
+{
+ gpg_error_t err = 0;
+ unsigned char radbuf[4];
+ int idx, quad_count;
+ FILE *fp;
+
+ if (!(state->flags & B64ENC_DID_HEADER))
+ goto cleanup;
+
+ /* Flush the base64 encoding */
+ fp = state->fp;
+ idx = state->idx;
+ quad_count = state->quad_count;
+ assert (idx < 4);
+ memcpy (radbuf, state->radbuf, idx);
+
+ if (idx)
+ {
+ char tmp[4];
+
+ tmp[0] = bintoasc[(*radbuf>>2)&077];
+ if (idx == 1)
+ {
+ tmp[1] = bintoasc[((*radbuf << 4) & 060) & 077];
+ tmp[2] = '=';
+ tmp[3] = '=';
+ }
+ else
+ {
+ tmp[1] = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+ tmp[2] = bintoasc[((radbuf[1] << 2) & 074) & 077];
+ tmp[3] = '=';
+ }
+ for (idx=0; idx < 4; idx++)
+ putc (tmp[idx], fp);
+ idx = 0;
+ if (ferror (fp))
+ goto write_error;
+
+ if (++quad_count >= (64/4))
+ {
+ quad_count = 0;
+ if (!(state->flags & B64ENC_NO_LINEFEEDS)
+ && fputs ("\n", fp) == EOF)
+ goto write_error;
+ }
+ }
+
+ /* Finish the last line and write the trailer. */
+ if (quad_count
+ && !(state->flags & B64ENC_NO_LINEFEEDS)
+ && fputs ("\n", fp) == EOF)
+ goto write_error;
+
+ if (state->title)
+ {
+ if ( fputs ("-----END ", fp) == EOF
+ || fputs (state->title, fp) == EOF
+ || fputs ("-----\n", fp) == EOF)
+ goto write_error;
+ }
+
+ goto cleanup;
+
+ write_error:
+ err = gpg_error_from_errno (errno);
+
+ cleanup:
+ if (state->title)
+ {
+ xfree (state->title);
+ state->title = NULL;
+ }
+ state->fp = NULL;
+ return err;
+}
+
diff --git a/common/dns-cert.c b/common/dns-cert.c
new file mode 100644
index 000000000..8dfcb9724
--- /dev/null
+++ b/common/dns-cert.c
@@ -0,0 +1,246 @@
+/* dns-cert.c - DNS CERT code
+ * Copyright (C) 2005, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <sys/types.h>
+#ifdef USE_DNS_CERT
+# ifdef HAVE_W32_SYSTEM
+# include <windows.h>
+# else
+# include <netinet/in.h>
+# include <arpa/nameser.h>
+# include <resolv.h>
+# endif
+#include <string.h>
+#endif
+
+#include "util.h"
+#include "iobuf.h"
+#include "dns-cert.h"
+
+/* Not every installation has gotten around to supporting CERTs
+ yet... */
+#ifndef T_CERT
+#define T_CERT 37
+#endif
+
+
+/* Returns -1 on error, 0 for no answer, 1 for PGP provided and 2 for
+ IPGP provided. */
+int
+get_dns_cert (const char *name,size_t max_size,IOBUF *iobuf,
+ unsigned char **fpr,size_t *fpr_len,char **url)
+{
+#ifdef USE_DNS_CERT
+ unsigned char *answer;
+ int r,ret=-1;
+ u16 count;
+
+ if(fpr)
+ *fpr=NULL;
+
+ if(url)
+ *url=NULL;
+
+ answer=xmalloc(max_size);
+
+ r=res_query(name,C_IN,T_CERT,answer,max_size);
+ /* Not too big, not too small, no errors and at least 1 answer. */
+ if(r>=sizeof(HEADER) && r<=max_size
+ && (((HEADER *)answer)->rcode)==NOERROR
+ && (count=ntohs(((HEADER *)answer)->ancount)))
+ {
+ int rc;
+ unsigned char *pt,*emsg;
+
+ emsg=&answer[r];
+
+ pt=&answer[sizeof(HEADER)];
+
+ /* Skip over the query */
+
+ rc=dn_skipname(pt,emsg);
+ if(rc==-1)
+ goto fail;
+
+ pt+=rc+QFIXEDSZ;
+
+ /* There are several possible response types for a CERT request.
+ We're interested in the PGP (a key) and IPGP (a URI) types.
+ Skip all others. TODO: A key is better than a URI since
+ we've gone through all this bother to fetch it, so favor that
+ if we have both PGP and IPGP? */
+
+ while(count-->0 && pt<emsg)
+ {
+ u16 type,class,dlen,ctype;
+
+ rc=dn_skipname(pt,emsg); /* the name we just queried for */
+ if(rc==-1)
+ break;
+
+ pt+=rc;
+
+ /* Truncated message? 15 bytes takes us to the point where
+ we start looking at the ctype. */
+ if((emsg-pt)<15)
+ break;
+
+ type=*pt++ << 8;
+ type|=*pt++;
+
+ class=*pt++ << 8;
+ class|=*pt++;
+ /* We asked for IN and got something else !? */
+ if(class!=C_IN)
+ break;
+
+ /* ttl */
+ pt+=4;
+
+ /* data length */
+ dlen=*pt++ << 8;
+ dlen|=*pt++;
+
+ /* We asked for CERT and got something else - might be a
+ CNAME, so loop around again. */
+ if(type!=T_CERT)
+ {
+ pt+=dlen;
+ continue;
+ }
+
+ /* The CERT type */
+ ctype=*pt++ << 8;
+ ctype|=*pt++;
+
+ /* Skip the CERT key tag and algo which we don't need. */
+ pt+=3;
+
+ dlen-=5;
+
+ /* 15 bytes takes us to here */
+
+ if(ctype==3 && iobuf && dlen)
+ {
+ /* PGP type */
+ *iobuf=iobuf_temp_with_content((char *)pt,dlen);
+ ret=1;
+ break;
+ }
+ else if(ctype==6 && dlen && dlen<1023 && dlen>=pt[0]+1
+ && fpr && fpr_len && url)
+ {
+ /* IPGP type */
+ *fpr_len=pt[0];
+
+ if(*fpr_len)
+ {
+ *fpr=xmalloc(*fpr_len);
+ memcpy(*fpr,&pt[1],*fpr_len);
+ }
+ else
+ *fpr=NULL;
+
+ if(dlen>*fpr_len+1)
+ {
+ *url=xmalloc(dlen-(*fpr_len+1)+1);
+ memcpy(*url,&pt[*fpr_len+1],dlen-(*fpr_len+1));
+ (*url)[dlen-(*fpr_len+1)]='\0';
+ }
+ else
+ *url=NULL;
+
+ ret=2;
+ break;
+ }
+
+ /* Neither type matches, so go around to the next answer. */
+ pt+=dlen;
+ }
+ }
+
+ fail:
+ xfree(answer);
+
+ return ret;
+#else /* !USE_DNS_CERT */
+ return -1;
+#endif
+}
+
+
+
+/* Test with simon.josefsson.org */
+
+#ifdef TEST
+int
+main(int argc,char *argv[])
+{
+ unsigned char *fpr;
+ size_t fpr_len;
+ char *url;
+ int rc;
+ IOBUF iobuf;
+
+ if(argc!=2)
+ {
+ printf("cert-test [name]\n");
+ return 1;
+ }
+
+ printf("CERT lookup on %s\n",argv[1]);
+
+ rc=get_dns_cert (argv[1],16384,&iobuf,&fpr,&fpr_len,&url);
+ if(rc==-1)
+ printf("error\n");
+ else if(rc==0)
+ printf("no answer\n");
+ else if(rc==1)
+ {
+ printf("key found: %d bytes\n",(int)iobuf_get_temp_length(iobuf));
+ iobuf_close(iobuf);
+ }
+ else if(rc==2)
+ {
+ if(fpr)
+ {
+ size_t i;
+ printf("Fingerprint found (%d bytes): ",(int)fpr_len);
+ for(i=0;i<fpr_len;i++)
+ printf("%02X",fpr[i]);
+ printf("\n");
+ }
+ else
+ printf("No fingerprint found\n");
+
+ if(url)
+ printf("URL found: %s\n",url);
+ else
+ printf("No URL found\n");
+
+ xfree(fpr);
+ xfree(url);
+ }
+
+ return 0;
+}
+#endif /* TEST */
diff --git a/common/dns-cert.h b/common/dns-cert.h
new file mode 100644
index 000000000..47e0d5a2d
--- /dev/null
+++ b/common/dns-cert.h
@@ -0,0 +1,28 @@
+/* dns-cert.h - DNS CERT definition
+ * Copyright (C) 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef GNUPG_COMMON_DNS_CERT_H
+#define GNUPG_COMMON_DNS_CERT_H
+
+int get_dns_cert (const char *name, size_t max_size, IOBUF *iobuf,
+ unsigned char **fpr, size_t *fpr_len, char **url);
+
+
+#endif /*GNUPG_COMMON_DNS_CERT_H*/
diff --git a/common/dynload.h b/common/dynload.h
new file mode 100644
index 000000000..9b67fa9ed
--- /dev/null
+++ b/common/dynload.h
@@ -0,0 +1,72 @@
+/* dlfcn.h - W32 functions for run-time dynamic loading
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_DYNLOAD_H
+#define GNUPG_DYNLOAD_H
+#ifndef __MINGW32__
+#include <dlfcn.h>
+#else
+#include <windows.h>
+
+#define RTLD_LAZY 0
+
+static inline void *
+dlopen (const char * name, int flag)
+{
+ void * hd = LoadLibrary (name);
+ return hd;
+}
+
+static inline void *
+dlsym (void *hd, const char *sym)
+{
+ if (hd && sym)
+ {
+ void * fnc = GetProcAddress (hd, sym);
+ if (!fnc)
+ return NULL;
+ return fnc;
+ }
+ return NULL;
+}
+
+
+static inline const char *
+dlerror (void)
+{
+ static char buf[32];
+ sprintf (buf, "ec=%lu", GetLastError ());
+ return buf;
+}
+
+
+static inline int
+dlclose (void * hd)
+{
+ if (hd)
+ {
+ CloseHandle (hd);
+ return 0;
+ }
+ return -1;
+}
+#endif /*__MINGW32__*/
+#endif /*GNUPG_DYNLOAD_H*/
diff --git a/common/errors.h b/common/errors.h
new file mode 100644
index 000000000..131891f65
--- /dev/null
+++ b/common/errors.h
@@ -0,0 +1,113 @@
+/* errors.h - Globally used error codes
+ * Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_ERRORS_H
+#define GNUPG_COMMON_ERRORS_H
+
+#include "util.h"
+
+/* Status codes - fixme: should go into another file */
+enum {
+ STATUS_ENTER,
+ STATUS_LEAVE,
+ STATUS_ABORT,
+ STATUS_GOODSIG,
+ STATUS_BADSIG,
+ STATUS_ERRSIG,
+ STATUS_BADARMOR,
+ STATUS_RSA_OR_IDEA,
+ STATUS_SIGEXPIRED,
+ STATUS_KEYREVOKED,
+ STATUS_TRUST_UNDEFINED,
+ STATUS_TRUST_NEVER,
+ STATUS_TRUST_MARGINAL,
+ STATUS_TRUST_FULLY,
+ STATUS_TRUST_ULTIMATE,
+
+ STATUS_SHM_INFO,
+ STATUS_SHM_GET,
+ STATUS_SHM_GET_BOOL,
+ STATUS_SHM_GET_HIDDEN,
+
+ STATUS_NEED_PASSPHRASE,
+ STATUS_VALIDSIG,
+ STATUS_SIG_ID,
+ STATUS_ENC_TO,
+ STATUS_NODATA,
+ STATUS_BAD_PASSPHRASE,
+ STATUS_NO_PUBKEY,
+ STATUS_NO_SECKEY,
+ STATUS_NEED_PASSPHRASE_SYM,
+ STATUS_DECRYPTION_FAILED,
+ STATUS_DECRYPTION_OKAY,
+ STATUS_MISSING_PASSPHRASE,
+ STATUS_GOOD_PASSPHRASE,
+ STATUS_GOODMDC,
+ STATUS_BADMDC,
+ STATUS_ERRMDC,
+ STATUS_IMPORTED,
+ STATUS_IMPORT_OK,
+ STATUS_IMPORT_PROBLEM,
+ STATUS_IMPORT_RES,
+ STATUS_FILE_START,
+ STATUS_FILE_DONE,
+ STATUS_FILE_ERROR,
+
+ STATUS_BEGIN_DECRYPTION,
+ STATUS_END_DECRYPTION,
+ STATUS_BEGIN_ENCRYPTION,
+ STATUS_END_ENCRYPTION,
+
+ STATUS_DELETE_PROBLEM,
+ STATUS_GET_BOOL,
+ STATUS_GET_LINE,
+ STATUS_GET_HIDDEN,
+ STATUS_GOT_IT,
+ STATUS_PROGRESS,
+ STATUS_SIG_CREATED,
+ STATUS_SESSION_KEY,
+ STATUS_NOTATION_NAME,
+ STATUS_NOTATION_DATA,
+ STATUS_POLICY_URL,
+ STATUS_BEGIN_STREAM,
+ STATUS_END_STREAM,
+ STATUS_KEY_CREATED,
+ STATUS_USERID_HIN,
+ STATUS_UNEXPECTED,
+ STATUS_INV_RECP,
+ STATUS_NO_RECP,
+ STATUS_ALREADY_SIGNED,
+
+ STATUS_EXPSIG,
+ STATUS_EXPKEYSIG,
+
+ STATUS_TRUNCATED,
+ STATUS_ERROR,
+ STATUS_NEWSIG
+};
+
+
+/*-- errors.c (build by mkerror and mkerrtok) --*/
+const char *gnupg_strerror (int err);
+const char *gnupg_error_token (int err);
+
+
+#endif /*GNUPG_COMMON_ERRORS_H*/
diff --git a/common/estream.c b/common/estream.c
new file mode 100644
index 000000000..c2030371b
--- /dev/null
+++ b/common/estream.c
@@ -0,0 +1,2615 @@
+/* estream.c - Extended stream I/O/ Library
+ * Copyright (C) 2004 g10 Code GmbH
+ *
+ * This file is part of Libestream.
+ *
+ * Libestream is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * Libestream 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 General Public License
+ * along with Libestream; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifdef USE_ESTREAM_SUPPORT_H
+# include <estream-support.h>
+#endif
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stddef.h>
+#include <assert.h>
+
+#ifdef HAVE_PTH
+# include <pth.h>
+#endif
+
+#ifndef HAVE_MKSTEMP
+int mkstemp (char *template);
+#endif
+
+#ifndef HAVE_MEMRCHR
+void *memrchr (const void *block, int c, size_t size);
+#endif
+
+#include <estream.h>
+
+
+
+/* Generally used types. */
+
+typedef void *(*func_realloc_t) (void *mem, size_t size);
+typedef void (*func_free_t) (void *mem);
+
+
+
+/* Buffer management layer. */
+
+#define BUFFER_BLOCK_SIZE BUFSIZ
+#define BUFFER_UNREAD_SIZE 16
+
+
+
+/* Macros. */
+
+#define BUFFER_ROUND_TO_BLOCK(size, block_size) \
+ (((size) + (block_size - 1)) / block_size)
+
+
+
+/* Locking. */
+
+#ifdef HAVE_PTH
+
+typedef pth_mutex_t estream_mutex_t;
+# define ESTREAM_MUTEX_INITIALIZER PTH_MUTEX_INIT
+# define ESTREAM_MUTEX_LOCK(mutex) \
+ pth_mutex_acquire (&(mutex), 0, NULL)
+# define ESTREAM_MUTEX_UNLOCK(mutex) \
+ pth_mutex_release (&(mutex))
+# define ESTREAM_MUTEX_TRYLOCK(mutex) \
+ ((pth_mutex_acquire (&(mutex), 1, NULL) == TRUE) ? 0 : -1)
+# define ESTREAM_MUTEX_INITIALIZE(mutex) \
+ pth_mutex_init (&(mutex))
+# define ESTREAM_THREADING_INIT() ((pth_init () == TRUE) ? 0 : -1)
+
+#else
+
+typedef void *estream_mutex_t;
+# define ESTREAM_MUTEX_INITIALIZER NULL
+# define ESTREAM_MUTEX_LOCK(mutex) (void) 0
+# define ESTREAM_MUTEX_UNLOCK(mutex) (void) 0
+# define ESTREAM_MUTEX_TRYLOCK(mutex) 0
+# define ESTREAM_MUTEX_INITIALIZE(mutex) (void) 0
+# define ESTREAM_THREADING_INIT() 0
+
+#endif
+
+/* Memory allocator functions. */
+
+#define MEM_ALLOC malloc
+#define MEM_REALLOC realloc
+#define MEM_FREE free
+
+/* Primitive system I/O. */
+
+#ifdef HAVE_PTH
+# define ESTREAM_SYS_READ pth_read
+# define ESTREAM_SYS_WRITE pth_write
+#else
+# define ESTREAM_SYS_READ read
+# define ESTREAM_SYS_WRITE write
+#endif
+
+/* Misc definitions. */
+
+#define ES_DEFAULT_OPEN_MODE (S_IRUSR | S_IWUSR)
+
+#define ES_FLAG_WRITING ES__FLAG_WRITING
+
+/* An internal stream object. */
+
+struct estream_internal
+{
+ unsigned char buffer[BUFFER_BLOCK_SIZE];
+ unsigned char unread_buffer[BUFFER_UNREAD_SIZE];
+ estream_mutex_t lock; /* Lock. */
+ void *cookie; /* Cookie. */
+ void *opaque; /* Opaque data. */
+ unsigned int flags; /* Flags. */
+ off_t offset;
+ es_cookie_read_function_t func_read;
+ es_cookie_write_function_t func_write;
+ es_cookie_seek_function_t func_seek;
+ es_cookie_close_function_t func_close;
+ int strategy;
+ int fd;
+ struct
+ {
+ unsigned int err: 1;
+ unsigned int eof: 1;
+ } indicators;
+ unsigned int deallocate_buffer: 1;
+};
+
+typedef struct estream_internal *estream_internal_t;
+
+#define ESTREAM_LOCK(stream) ESTREAM_MUTEX_LOCK (stream->intern->lock)
+#define ESTREAM_UNLOCK(stream) ESTREAM_MUTEX_UNLOCK (stream->intern->lock)
+#define ESTREAM_TRYLOCK(stream) ESTREAM_MUTEX_TRYLOCK (stream->intern->lock)
+
+/* Stream list. */
+
+typedef struct estream_list *estream_list_t;
+
+struct estream_list
+{
+ estream_t car;
+ estream_list_t cdr;
+ estream_list_t *prev_cdr;
+};
+
+static estream_list_t estream_list;
+#ifdef HAVE_PTH
+static estream_mutex_t estream_list_lock = ESTREAM_MUTEX_INITIALIZER;
+#endif
+
+#define ESTREAM_LIST_LOCK ESTREAM_MUTEX_LOCK (estream_list_lock)
+#define ESTREAM_LIST_UNLOCK ESTREAM_MUTEX_UNLOCK (estream_list_lock)
+
+#ifndef EOPNOTSUPP
+# define EOPNOTSUPP ENOSYS
+#endif
+
+
+
+
+/* Macros. */
+
+/* Calculate array dimension. */
+#define DIM(array) (sizeof (array) / sizeof (*array))
+
+/* Evaluate EXPRESSION, setting VARIABLE to the return code, if
+ VARIABLE is zero. */
+#define SET_UNLESS_NONZERO(variable, tmp_variable, expression) \
+ do \
+ { \
+ tmp_variable = expression; \
+ if ((! variable) && tmp_variable) \
+ variable = tmp_variable; \
+ } \
+ while (0)
+
+/*
+ * List manipulation.
+ */
+
+/* Add STREAM to the list of registered stream objects. */
+static int
+es_list_add (estream_t stream)
+{
+ estream_list_t list_obj;
+ int ret;
+
+ list_obj = MEM_ALLOC (sizeof (*list_obj));
+ if (! list_obj)
+ ret = -1;
+ else
+ {
+ ESTREAM_LIST_LOCK;
+ list_obj->car = stream;
+ list_obj->cdr = estream_list;
+ list_obj->prev_cdr = &estream_list;
+ if (estream_list)
+ estream_list->prev_cdr = &list_obj->cdr;
+ estream_list = list_obj;
+ ESTREAM_LIST_UNLOCK;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Remove STREAM from the list of registered stream objects. */
+static void
+es_list_remove (estream_t stream)
+{
+ estream_list_t list_obj;
+
+ ESTREAM_LIST_LOCK;
+ for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
+ if (list_obj->car == stream)
+ {
+ *list_obj->prev_cdr = list_obj->cdr;
+ if (list_obj->cdr)
+ list_obj->cdr->prev_cdr = list_obj->prev_cdr;
+ MEM_FREE (list_obj);
+ break;
+ }
+ ESTREAM_LIST_UNLOCK;
+}
+
+/* Type of an stream-iterator-function. */
+typedef int (*estream_iterator_t) (estream_t stream);
+
+/* Iterate over list of registered streams, calling ITERATOR for each
+ of them. */
+static int
+es_list_iterate (estream_iterator_t iterator)
+{
+ estream_list_t list_obj;
+ int ret = 0;
+
+ ESTREAM_LIST_LOCK;
+ for (list_obj = estream_list; list_obj; list_obj = list_obj->cdr)
+ ret |= (*iterator) (list_obj->car);
+ ESTREAM_LIST_UNLOCK;
+
+ return ret;
+}
+
+
+
+/*
+ * Initialization.
+ */
+
+static int
+es_init_do (void)
+{
+ int err;
+
+ err = ESTREAM_THREADING_INIT ();
+
+ return err;
+}
+
+
+
+/*
+ * I/O methods.
+ */
+
+/* Implementation of Memory I/O. */
+
+/* Cookie for memory objects. */
+typedef struct estream_cookie_mem
+{
+ unsigned int flags; /* Open flags. */
+ unsigned char *memory; /* Data. */
+ size_t memory_size; /* Size of MEMORY. */
+ size_t offset; /* Current offset in MEMORY. */
+ size_t data_len; /* Length of data in MEMORY. */
+ size_t block_size; /* Block size. */
+ unsigned int grow: 1; /* MEMORY is allowed to grow. */
+ unsigned int append_zero: 1; /* Append zero after data. */
+ unsigned int dont_free: 1; /* Append zero after data. */
+ char **ptr;
+ size_t *size;
+ func_realloc_t func_realloc;
+ func_free_t func_free;
+} *estream_cookie_mem_t;
+
+/* Create function for memory objects. */
+static int
+es_func_mem_create (void *ES__RESTRICT *ES__RESTRICT cookie,
+ unsigned char *ES__RESTRICT data, size_t data_n,
+ size_t data_len,
+ size_t block_size, unsigned int grow,
+ unsigned int append_zero, unsigned int dont_free,
+ char **ptr, size_t *size,
+ func_realloc_t func_realloc, func_free_t func_free,
+ unsigned int flags)
+{
+ estream_cookie_mem_t mem_cookie;
+ int err;
+
+ mem_cookie = MEM_ALLOC (sizeof (*mem_cookie));
+ if (! mem_cookie)
+ err = -1;
+ else
+ {
+ mem_cookie->flags = flags;
+ mem_cookie->memory = data;
+ mem_cookie->memory_size = data_n;
+ mem_cookie->offset = 0;
+ mem_cookie->data_len = data_len;
+ mem_cookie->block_size = block_size;
+ mem_cookie->grow = grow ? 1 : 0;
+ mem_cookie->append_zero = append_zero ? 1 : 0;
+ mem_cookie->dont_free = dont_free ? 1 : 0;
+ mem_cookie->ptr = ptr;
+ mem_cookie->size = size;
+ mem_cookie->func_realloc = func_realloc ? func_realloc : MEM_REALLOC;
+ mem_cookie->func_free = func_free ? func_free : MEM_FREE;
+ mem_cookie->offset = 0;
+ *cookie = mem_cookie;
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Read function for memory objects. */
+static ssize_t
+es_func_mem_read (void *cookie, void *buffer, size_t size)
+{
+ estream_cookie_mem_t mem_cookie = cookie;
+ ssize_t ret;
+
+ if (size > mem_cookie->data_len - mem_cookie->offset)
+ size = mem_cookie->data_len - mem_cookie->offset;
+
+ if (size)
+ {
+ memcpy (buffer, mem_cookie->memory + mem_cookie->offset, size);
+ mem_cookie->offset += size;
+ }
+
+ ret = size;
+
+ return ret;
+}
+
+/* Write function for memory objects. */
+static ssize_t
+es_func_mem_write (void *cookie, const void *buffer, size_t size)
+{
+ estream_cookie_mem_t mem_cookie = cookie;
+ func_realloc_t func_realloc = mem_cookie->func_realloc;
+ unsigned char *memory_new;
+ size_t newsize;
+ ssize_t ret;
+ int err;
+
+ if (size)
+ {
+ /* Regular write. */
+
+ if (mem_cookie->flags & O_APPEND)
+ /* Append to data. */
+ mem_cookie->offset = mem_cookie->data_len;
+
+ if (! mem_cookie->grow)
+ if (size > mem_cookie->memory_size - mem_cookie->offset)
+ size = mem_cookie->memory_size - mem_cookie->offset;
+
+ err = 0;
+
+ while (size > (mem_cookie->memory_size - mem_cookie->offset))
+ {
+ memory_new = (*func_realloc) (mem_cookie->memory,
+ mem_cookie->memory_size
+ + mem_cookie->block_size);
+ if (! memory_new)
+ {
+ err = -1;
+ break;
+ }
+ else
+ {
+ if (mem_cookie->memory != memory_new)
+ mem_cookie->memory = memory_new;
+ mem_cookie->memory_size += mem_cookie->block_size;
+ }
+ }
+ if (err)
+ goto out;
+
+ if (size)
+ {
+ memcpy (mem_cookie->memory + mem_cookie->offset, buffer, size);
+ if (mem_cookie->offset + size > mem_cookie->data_len)
+ mem_cookie->data_len = mem_cookie->offset + size;
+ mem_cookie->offset += size;
+ }
+ }
+ else
+ {
+ /* Flush. */
+
+ err = 0;
+ if (mem_cookie->append_zero)
+ {
+ if (mem_cookie->data_len >= mem_cookie->memory_size)
+ {
+ newsize = BUFFER_ROUND_TO_BLOCK (mem_cookie->data_len + 1,
+ mem_cookie->block_size)
+ * mem_cookie->block_size;
+
+ memory_new = (*func_realloc) (mem_cookie->memory, newsize);
+ if (! memory_new)
+ {
+ err = -1;
+ goto out;
+ }
+
+ if (mem_cookie->memory != memory_new)
+ mem_cookie->memory = memory_new;
+ mem_cookie->memory_size = newsize;
+ }
+
+ mem_cookie->memory[mem_cookie->data_len + 1] = 0;
+ }
+
+ /* Return information to user if necessary. */
+ if (mem_cookie->ptr)
+ *mem_cookie->ptr = (char *) mem_cookie->memory;
+ if (mem_cookie->size)
+ *mem_cookie->size = mem_cookie->data_len;
+ }
+
+ out:
+
+ if (err)
+ ret = -1;
+ else
+ ret = size;
+
+ return ret;
+}
+
+/* Seek function for memory objects. */
+static int
+es_func_mem_seek (void *cookie, off_t *offset, int whence)
+{
+ estream_cookie_mem_t mem_cookie = cookie;
+ off_t pos_new;
+ int err = 0;
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ pos_new = *offset;
+ break;
+
+ case SEEK_CUR:
+ pos_new = mem_cookie->offset += *offset;
+ break;
+
+ case SEEK_END:
+ pos_new = mem_cookie->data_len += *offset;
+ break;
+
+ default:
+ /* Never reached. */
+ pos_new = 0;
+ }
+
+ if (pos_new > mem_cookie->memory_size)
+ {
+ /* Grow buffer if possible. */
+
+ if (mem_cookie->grow)
+ {
+ func_realloc_t func_realloc = mem_cookie->func_realloc;
+ size_t newsize;
+ void *p;
+
+ newsize = BUFFER_ROUND_TO_BLOCK (pos_new, mem_cookie->block_size);
+ p = (*func_realloc) (mem_cookie->memory, newsize);
+ if (! p)
+ {
+ err = -1;
+ goto out;
+ }
+ else
+ {
+ if (mem_cookie->memory != p)
+ mem_cookie->memory = p;
+ mem_cookie->memory_size = newsize;
+ }
+ }
+ else
+ {
+ errno = EINVAL;
+ err = -1;
+ goto out;
+ }
+ }
+
+ if (pos_new > mem_cookie->data_len)
+ /* Fill spare space with zeroes. */
+ memset (mem_cookie->memory + mem_cookie->data_len,
+ 0, pos_new - mem_cookie->data_len);
+
+ mem_cookie->offset = pos_new;
+ *offset = pos_new;
+
+ out:
+
+ return err;
+}
+
+/* Destroy function for memory objects. */
+static int
+es_func_mem_destroy (void *cookie)
+{
+ estream_cookie_mem_t mem_cookie = cookie;
+ func_free_t func_free = mem_cookie->func_free;
+
+ if (! mem_cookie->dont_free)
+ (*func_free) (mem_cookie->memory);
+ MEM_FREE (mem_cookie);
+
+ return 0;
+}
+
+static es_cookie_io_functions_t estream_functions_mem =
+ {
+ es_func_mem_read,
+ es_func_mem_write,
+ es_func_mem_seek,
+ es_func_mem_destroy,
+ };
+
+/* Implementation of fd I/O. */
+
+/* Cookie for fd objects. */
+typedef struct estream_cookie_fd
+{
+ int fd;
+} *estream_cookie_fd_t;
+
+/* Create function for fd objects. */
+static int
+es_func_fd_create (void **cookie, int fd, unsigned int flags)
+{
+ estream_cookie_fd_t fd_cookie;
+ int err;
+
+ fd_cookie = MEM_ALLOC (sizeof (*fd_cookie));
+ if (! fd_cookie)
+ err = -1;
+ else
+ {
+ fd_cookie->fd = fd;
+ *cookie = fd_cookie;
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Read function for fd objects. */
+static ssize_t
+es_func_fd_read (void *cookie, void *buffer, size_t size)
+
+{
+ estream_cookie_fd_t file_cookie = cookie;
+ ssize_t bytes_read;
+
+ do
+ bytes_read = ESTREAM_SYS_READ (file_cookie->fd, buffer, size);
+ while (bytes_read == -1 && errno == EINTR);
+
+ return bytes_read;
+}
+
+/* Write function for fd objects. */
+static ssize_t
+es_func_fd_write (void *cookie, const void *buffer, size_t size)
+
+{
+ estream_cookie_fd_t file_cookie = cookie;
+ ssize_t bytes_written;
+
+ do
+ bytes_written = ESTREAM_SYS_WRITE (file_cookie->fd, buffer, size);
+ while (bytes_written == -1 && errno == EINTR);
+
+ return bytes_written;
+}
+
+/* Seek function for fd objects. */
+static int
+es_func_fd_seek (void *cookie, off_t *offset, int whence)
+{
+ estream_cookie_fd_t file_cookie = cookie;
+ off_t offset_new;
+ int err;
+
+ offset_new = lseek (file_cookie->fd, *offset, whence);
+ if (offset_new == -1)
+ err = -1;
+ else
+ {
+ *offset = offset_new;
+ err = 0;
+ }
+
+ return err;
+}
+
+/* Destroy function for fd objects. */
+static int
+es_func_fd_destroy (void *cookie)
+{
+ estream_cookie_fd_t fd_cookie = cookie;
+ int err;
+
+ if (fd_cookie)
+ {
+ err = close (fd_cookie->fd);
+ MEM_FREE (fd_cookie);
+ }
+ else
+ err = 0;
+
+ return err;
+}
+
+static es_cookie_io_functions_t estream_functions_fd =
+ {
+ es_func_fd_read,
+ es_func_fd_write,
+ es_func_fd_seek,
+ es_func_fd_destroy
+ };
+
+/* Implementation of file I/O. */
+
+/* Create function for file objects. */
+static int
+es_func_file_create (void **cookie, int *filedes,
+ const char *path, unsigned int flags)
+{
+ estream_cookie_fd_t file_cookie;
+ int err;
+ int fd;
+
+ err = 0;
+ fd = -1;
+
+ file_cookie = MEM_ALLOC (sizeof (*file_cookie));
+ if (! file_cookie)
+ {
+ err = -1;
+ goto out;
+ }
+
+ fd = open (path, flags, ES_DEFAULT_OPEN_MODE);
+ if (fd == -1)
+ {
+ err = -1;
+ goto out;
+ }
+
+ file_cookie->fd = fd;
+ *cookie = file_cookie;
+ *filedes = fd;
+
+ out:
+
+ if (err)
+ MEM_FREE (file_cookie);
+
+ return err;
+}
+
+static es_cookie_io_functions_t estream_functions_file =
+ {
+ es_func_fd_read,
+ es_func_fd_write,
+ es_func_fd_seek,
+ es_func_fd_destroy
+ };
+
+
+
+/* Stream primitives. */
+
+static int
+es_convert_mode (const char *mode, unsigned int *flags)
+{
+ struct
+ {
+ const char *mode;
+ unsigned int flags;
+ } mode_flags[] = { { "r",
+ O_RDONLY },
+ { "rb",
+ O_RDONLY },
+ { "w",
+ O_WRONLY | O_TRUNC | O_CREAT },
+ { "wb",
+ O_WRONLY | O_TRUNC | O_CREAT },
+ { "a",
+ O_WRONLY | O_APPEND | O_CREAT },
+ { "ab",
+ O_WRONLY | O_APPEND | O_CREAT },
+ { "r+",
+ O_RDWR },
+ { "rb+",
+ O_RDWR },
+ { "r+b",
+ O_RDONLY | O_WRONLY },
+ { "w+",
+ O_RDWR | O_TRUNC | O_CREAT },
+ { "wb+",
+ O_RDWR | O_TRUNC | O_CREAT },
+ { "w+b",
+ O_RDWR | O_TRUNC | O_CREAT },
+ { "a+",
+ O_RDWR | O_CREAT | O_APPEND },
+ { "ab+",
+ O_RDWR | O_CREAT | O_APPEND },
+ { "a+b",
+ O_RDWR | O_CREAT | O_APPEND } };
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < DIM (mode_flags); i++)
+ if (! strcmp (mode_flags[i].mode, mode))
+ break;
+ if (i == DIM (mode_flags))
+ {
+ errno = EINVAL;
+ err = -1;
+ }
+ else
+ {
+ err = 0;
+ *flags = mode_flags[i].flags;
+ }
+
+ return err;
+}
+
+
+
+/*
+ * Low level stream functionality.
+ */
+
+static int
+es_fill (estream_t stream)
+{
+ size_t bytes_read = 0;
+ int err;
+
+ if (!stream->intern->func_read)
+ {
+ errno = EOPNOTSUPP;
+ err = -1;
+ }
+ else
+ {
+ es_cookie_read_function_t func_read = stream->intern->func_read;
+ ssize_t ret;
+
+ ret = (*func_read) (stream->intern->cookie,
+ stream->buffer, stream->buffer_size);
+ if (ret == -1)
+ {
+ bytes_read = 0;
+ err = -1;
+ }
+ else
+ {
+ bytes_read = ret;
+ err = 0;
+ }
+ }
+
+ if (err)
+ stream->intern->indicators.err = 1;
+ else if (!bytes_read)
+ stream->intern->indicators.eof = 1;
+
+ stream->intern->offset += stream->data_len;
+ stream->data_len = bytes_read;
+ stream->data_offset = 0;
+
+ return err;
+}
+
+static int
+es_flush (estream_t stream)
+{
+ es_cookie_write_function_t func_write = stream->intern->func_write;
+ int err;
+
+ assert (stream->flags & ES_FLAG_WRITING);
+
+ if (stream->data_offset)
+ {
+ size_t bytes_written;
+ size_t data_flushed;
+ ssize_t ret;
+
+ if (! func_write)
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* Note: to prevent an endless loop caused by user-provided
+ write-functions that pretend to have written more bytes than
+ they were asked to write, we have to check for
+ "(stream->data_offset - data_flushed) > 0" instead of
+ "stream->data_offset - data_flushed". */
+
+ data_flushed = 0;
+ err = 0;
+
+ while ((((ssize_t) (stream->data_offset - data_flushed)) > 0) && (! err))
+ {
+ ret = (*func_write) (stream->intern->cookie,
+ stream->buffer + data_flushed,
+ stream->data_offset - data_flushed);
+ if (ret == -1)
+ {
+ bytes_written = 0;
+ err = -1;
+ }
+ else
+ bytes_written = ret;
+
+ data_flushed += bytes_written;
+ if (err)
+ break;
+ }
+
+ stream->data_flushed += data_flushed;
+ if (stream->data_offset == data_flushed)
+ {
+ stream->intern->offset += stream->data_offset;
+ stream->data_offset = 0;
+ stream->data_flushed = 0;
+
+ /* Propagate flush event. */
+ (*func_write) (stream->intern->cookie, NULL, 0);
+ }
+ }
+ else
+ err = 0;
+
+ out:
+
+ if (err)
+ stream->intern->indicators.err = 1;
+
+ return err;
+}
+
+/* Discard buffered data for STREAM. */
+static void
+es_empty (estream_t stream)
+{
+ assert (! (stream->flags & ES_FLAG_WRITING));
+ stream->data_len = 0;
+ stream->data_offset = 0;
+ stream->unread_data_len = 0;
+}
+
+/* Initialize STREAM. */
+static void
+es_initialize (estream_t stream,
+ void *cookie, int fd, es_cookie_io_functions_t functions)
+{
+ stream->intern->cookie = cookie;
+ stream->intern->opaque = NULL;
+ stream->intern->offset = 0;
+ stream->intern->func_read = functions.func_read;
+ stream->intern->func_write = functions.func_write;
+ stream->intern->func_seek = functions.func_seek;
+ stream->intern->func_close = functions.func_close;
+ stream->intern->strategy = _IOFBF;
+ stream->intern->fd = fd;
+ stream->intern->indicators.err = 0;
+ stream->intern->indicators.eof = 0;
+ stream->intern->deallocate_buffer = 0;
+
+ stream->data_len = 0;
+ stream->data_offset = 0;
+ stream->data_flushed = 0;
+ stream->unread_data_len = 0;
+ stream->flags = 0;
+}
+
+/* Deinitialize STREAM. */
+static int
+es_deinitialize (estream_t stream)
+{
+ es_cookie_close_function_t func_close;
+ int err, tmp_err;
+
+ func_close = stream->intern->func_close;
+
+ err = 0;
+ if (stream->flags & ES_FLAG_WRITING)
+ SET_UNLESS_NONZERO (err, tmp_err, es_flush (stream));
+ if (func_close)
+ SET_UNLESS_NONZERO (err, tmp_err, (*func_close) (stream->intern->cookie));
+
+ return err;
+}
+
+/* Create a new stream object, initialize it. */
+static int
+es_create (estream_t *stream, void *cookie, int fd,
+ es_cookie_io_functions_t functions)
+{
+ estream_internal_t stream_internal_new;
+ estream_t stream_new;
+ int err;
+
+ stream_new = NULL;
+ stream_internal_new = NULL;
+
+ stream_new = MEM_ALLOC (sizeof (*stream_new));
+ if (! stream_new)
+ {
+ err = -1;
+ goto out;
+ }
+
+ stream_internal_new = MEM_ALLOC (sizeof (*stream_internal_new));
+ if (! stream_internal_new)
+ {
+ err = -1;
+ goto out;
+ }
+
+ stream_new->buffer = stream_internal_new->buffer;
+ stream_new->buffer_size = sizeof (stream_internal_new->buffer);
+ stream_new->unread_buffer = stream_internal_new->unread_buffer;
+ stream_new->unread_buffer_size = sizeof (stream_internal_new->unread_buffer);
+ stream_new->intern = stream_internal_new;
+
+ ESTREAM_MUTEX_INITIALIZE (stream_new->intern->lock);
+ es_initialize (stream_new, cookie, fd, functions);
+
+ err = es_list_add (stream_new);
+ if (err)
+ goto out;
+
+ *stream = stream_new;
+
+ out:
+
+ if (err)
+ {
+ if (stream_new)
+ {
+ es_deinitialize (stream_new);
+ MEM_FREE (stream_new);
+ }
+ }
+
+ return err;
+}
+
+/* Deinitialize a stream object and destroy it. */
+static int
+es_destroy (estream_t stream)
+{
+ int err = 0;
+
+ if (stream)
+ {
+ es_list_remove (stream);
+ err = es_deinitialize (stream);
+ MEM_FREE (stream->intern);
+ MEM_FREE (stream);
+ }
+
+ return err;
+}
+
+/* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
+ unbuffered-mode, storing the amount of bytes read in
+ *BYTES_READ. */
+static int
+es_read_nbf (estream_t ES__RESTRICT stream,
+ unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
+{
+ es_cookie_read_function_t func_read = stream->intern->func_read;
+ size_t data_read;
+ ssize_t ret;
+ int err;
+
+ data_read = 0;
+ err = 0;
+
+ while (bytes_to_read - data_read)
+ {
+ ret = (*func_read) (stream->intern->cookie,
+ buffer + data_read, bytes_to_read - data_read);
+ if (ret == -1)
+ {
+ err = -1;
+ break;
+ }
+ else if (ret)
+ data_read += ret;
+ else
+ break;
+ }
+
+ stream->intern->offset += data_read;
+ *bytes_read = data_read;
+
+ return err;
+}
+
+/* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
+ fully-buffered-mode, storing the amount of bytes read in
+ *BYTES_READ. */
+static int
+es_read_fbf (estream_t ES__RESTRICT stream,
+ unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
+{
+ size_t data_available;
+ size_t data_to_read;
+ size_t data_read;
+ int err;
+
+ data_read = 0;
+ err = 0;
+
+ while ((bytes_to_read - data_read) && (! err))
+ {
+ if (stream->data_offset == stream->data_len)
+ {
+ /* Nothing more to read in current container, try to
+ fill container with new data. */
+ err = es_fill (stream);
+ if (! err)
+ if (! stream->data_len)
+ /* Filling did not result in any data read. */
+ break;
+ }
+
+ if (! err)
+ {
+ /* Filling resulted in some new data. */
+
+ data_to_read = bytes_to_read - data_read;
+ data_available = stream->data_len - stream->data_offset;
+ if (data_to_read > data_available)
+ data_to_read = data_available;
+
+ memcpy (buffer + data_read,
+ stream->buffer + stream->data_offset, data_to_read);
+ stream->data_offset += data_to_read;
+ data_read += data_to_read;
+ }
+ }
+
+ *bytes_read = data_read;
+
+ return err;
+}
+
+/* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER in
+ line-buffered-mode, storing the amount of bytes read in
+ *BYTES_READ. */
+static int
+es_read_lbf (estream_t ES__RESTRICT stream,
+ unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
+{
+ int err;
+
+ err = es_read_fbf (stream, buffer, bytes_to_read, bytes_read);
+
+ return err;
+}
+
+/* Try to read BYTES_TO_READ bytes FROM STREAM into BUFFER, storing
+ *the amount of bytes read in BYTES_READ. */
+static int
+es_readn (estream_t ES__RESTRICT stream,
+ void *ES__RESTRICT buffer_arg,
+ size_t bytes_to_read, size_t *ES__RESTRICT bytes_read)
+{
+ unsigned char *buffer = (unsigned char *)buffer_arg;
+ size_t data_read_unread, data_read;
+ int err;
+
+ data_read_unread = 0;
+ data_read = 0;
+ err = 0;
+
+ if (stream->flags & ES_FLAG_WRITING)
+ {
+ /* Switching to reading mode -> flush output. */
+ err = es_flush (stream);
+ if (err)
+ goto out;
+ stream->flags &= ~ES_FLAG_WRITING;
+ }
+
+ /* Read unread data first. */
+ while ((bytes_to_read - data_read_unread) && stream->unread_data_len)
+ {
+ buffer[data_read_unread]
+ = stream->unread_buffer[stream->unread_data_len - 1];
+ stream->unread_data_len--;
+ data_read_unread++;
+ }
+
+ switch (stream->intern->strategy)
+ {
+ case _IONBF:
+ err = es_read_nbf (stream,
+ buffer + data_read_unread,
+ bytes_to_read - data_read_unread, &data_read);
+ break;
+ case _IOLBF:
+ err = es_read_lbf (stream,
+ buffer + data_read_unread,
+ bytes_to_read - data_read_unread, &data_read);
+ break;
+ case _IOFBF:
+ err = es_read_fbf (stream,
+ buffer + data_read_unread,
+ bytes_to_read - data_read_unread, &data_read);
+ break;
+ }
+
+ out:
+
+ if (bytes_read)
+ *bytes_read = data_read_unread + data_read;
+
+ return err;
+}
+
+/* Try to unread DATA_N bytes from DATA into STREAM, storing the
+ amount of bytes succesfully unread in *BYTES_UNREAD. */
+static void
+es_unreadn (estream_t ES__RESTRICT stream,
+ const unsigned char *ES__RESTRICT data, size_t data_n,
+ size_t *ES__RESTRICT bytes_unread)
+{
+ size_t space_left;
+
+ space_left = stream->unread_buffer_size - stream->unread_data_len;
+
+ if (data_n > space_left)
+ data_n = space_left;
+
+ if (! data_n)
+ goto out;
+
+ memcpy (stream->unread_buffer + stream->unread_data_len, data, data_n);
+ stream->unread_data_len += data_n;
+ stream->intern->indicators.eof = 0;
+
+ out:
+
+ if (bytes_unread)
+ *bytes_unread = data_n;
+}
+
+/* Seek in STREAM. */
+static int
+es_seek (estream_t ES__RESTRICT stream, off_t offset, int whence,
+ off_t *ES__RESTRICT offset_new)
+{
+ es_cookie_seek_function_t func_seek = stream->intern->func_seek;
+ int err, ret;
+ off_t off;
+
+ if (! func_seek)
+ {
+ errno = EOPNOTSUPP;
+ err = -1;
+ goto out;
+ }
+
+ if (stream->flags & ES_FLAG_WRITING)
+ {
+ /* Flush data first in order to prevent flushing it to the wrong
+ offset. */
+ err = es_flush (stream);
+ if (err)
+ goto out;
+ stream->flags &= ~ES_FLAG_WRITING;
+ }
+
+ off = offset;
+ if (whence == SEEK_CUR)
+ {
+ off = off - stream->data_len + stream->data_offset;
+ off -= stream->unread_data_len;
+ }
+
+ ret = (*func_seek) (stream->intern->cookie, &off, whence);
+ if (ret == -1)
+ {
+ err = -1;
+ goto out;
+ }
+
+ err = 0;
+ es_empty (stream);
+
+ if (offset_new)
+ *offset_new = off;
+
+ stream->intern->indicators.eof = 0;
+ stream->intern->offset = off;
+
+ out:
+
+ if (err)
+ stream->intern->indicators.err = 1;
+
+ return err;
+}
+
+/* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
+ unbuffered-mode, storing the amount of bytes written in
+ *BYTES_WRITTEN. */
+static int
+es_write_nbf (estream_t ES__RESTRICT stream,
+ const unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
+{
+ es_cookie_write_function_t func_write = stream->intern->func_write;
+ size_t data_written;
+ ssize_t ret;
+ int err;
+
+ if (bytes_to_write && (! func_write))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ data_written = 0;
+ err = 0;
+
+ while (bytes_to_write - data_written)
+ {
+ ret = (*func_write) (stream->intern->cookie,
+ buffer + data_written,
+ bytes_to_write - data_written);
+ if (ret == -1)
+ {
+ err = -1;
+ break;
+ }
+ else
+ data_written += ret;
+ }
+
+ stream->intern->offset += data_written;
+ *bytes_written = data_written;
+
+ out:
+
+ return err;
+}
+
+/* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
+ fully-buffered-mode, storing the amount of bytes written in
+ *BYTES_WRITTEN. */
+static int
+es_write_fbf (estream_t ES__RESTRICT stream,
+ const unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
+{
+ size_t space_available;
+ size_t data_to_write;
+ size_t data_written;
+ int err;
+
+ data_written = 0;
+ err = 0;
+
+ while ((bytes_to_write - data_written) && (! err))
+ {
+ if (stream->data_offset == stream->buffer_size)
+ /* Container full, flush buffer. */
+ err = es_flush (stream);
+
+ if (! err)
+ {
+ /* Flushing resulted in empty container. */
+
+ data_to_write = bytes_to_write - data_written;
+ space_available = stream->buffer_size - stream->data_offset;
+ if (data_to_write > space_available)
+ data_to_write = space_available;
+
+ memcpy (stream->buffer + stream->data_offset,
+ buffer + data_written, data_to_write);
+ stream->data_offset += data_to_write;
+ data_written += data_to_write;
+ }
+ }
+
+ *bytes_written = data_written;
+
+ return err;
+}
+
+
+/* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in
+ line-buffered-mode, storing the amount of bytes written in
+ *BYTES_WRITTEN. */
+static int
+es_write_lbf (estream_t ES__RESTRICT stream,
+ const unsigned char *ES__RESTRICT buffer,
+ size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
+{
+ size_t data_flushed = 0;
+ size_t data_buffered = 0;
+ unsigned char *nlp;
+ int err = 0;
+
+ nlp = memrchr (buffer, '\n', bytes_to_write);
+ if (nlp)
+ {
+ /* Found a newline, directly write up to (including) this
+ character. */
+ err = es_flush (stream);
+ if (!err)
+ err = es_write_nbf (stream, buffer, nlp - buffer + 1, &data_flushed);
+ }
+
+ if (!err)
+ {
+ /* Write remaining data fully buffered. */
+ err = es_write_fbf (stream, buffer + data_flushed,
+ bytes_to_write - data_flushed, &data_buffered);
+ }
+
+ *bytes_written = data_flushed + data_buffered;
+ return err;
+}
+
+
+/* Write BYTES_TO_WRITE bytes from BUFFER into STREAM in, storing the
+ amount of bytes written in BYTES_WRITTEN. */
+static int
+es_writen (estream_t ES__RESTRICT stream,
+ const void *ES__RESTRICT buffer,
+ size_t bytes_to_write, size_t *ES__RESTRICT bytes_written)
+{
+ size_t data_written;
+ int err;
+
+ data_written = 0;
+ err = 0;
+
+ if (! (stream->flags & ES_FLAG_WRITING))
+ {
+ /* Switching to writing mode -> discard input data and seek to
+ position at which reading has stopped. */
+
+ err = es_seek (stream, 0, SEEK_CUR, NULL);
+ if (err)
+ {
+ if (errno == ESPIPE)
+ err = 0;
+ else
+ goto out;
+ }
+ }
+
+ switch (stream->intern->strategy)
+ {
+ case _IONBF:
+ err = es_write_nbf (stream, buffer, bytes_to_write, &data_written);
+ break;
+
+ case _IOLBF:
+ err = es_write_lbf (stream, buffer, bytes_to_write, &data_written);
+ break;
+
+ case _IOFBF:
+ err = es_write_fbf (stream, buffer, bytes_to_write, &data_written);
+ break;
+ }
+
+ out:
+
+ if (bytes_written)
+ *bytes_written = data_written;
+ if (data_written)
+ if (! (stream->flags & ES_FLAG_WRITING))
+ stream->flags |= ES_FLAG_WRITING;
+
+ return err;
+}
+
+
+static int
+es_peek (estream_t ES__RESTRICT stream, unsigned char **ES__RESTRICT data,
+ size_t *ES__RESTRICT data_len)
+{
+ int err;
+
+ if (stream->flags & ES_FLAG_WRITING)
+ {
+ /* Switching to reading mode -> flush output. */
+ err = es_flush (stream);
+ if (err)
+ goto out;
+ stream->flags &= ~ES_FLAG_WRITING;
+ }
+
+ if (stream->data_offset == stream->data_len)
+ {
+ /* Refill container. */
+ err = es_fill (stream);
+ if (err)
+ goto out;
+ }
+
+ if (data)
+ *data = stream->buffer + stream->data_offset;
+ if (data_len)
+ *data_len = stream->data_len - stream->data_offset;
+ err = 0;
+
+ out:
+
+ return err;
+}
+
+
+/* Skip SIZE bytes of input data contained in buffer. */
+static int
+es_skip (estream_t stream, size_t size)
+{
+ int err;
+
+ if (stream->data_offset + size > stream->data_len)
+ {
+ errno = EINVAL;
+ err = -1;
+ }
+ else
+ {
+ stream->data_offset += size;
+ err = 0;
+ }
+
+ return err;
+}
+
+
+static int
+es_read_line (estream_t ES__RESTRICT stream, size_t max_length,
+ char *ES__RESTRICT *ES__RESTRICT line,
+ size_t *ES__RESTRICT line_length)
+{
+ size_t space_left;
+ size_t line_size;
+ estream_t line_stream;
+ char *line_new;
+ void *line_stream_cookie;
+ char *newline;
+ unsigned char *data;
+ size_t data_len;
+ int err;
+
+ line_new = NULL;
+ line_stream = NULL;
+ line_stream_cookie = NULL;
+
+ err = es_func_mem_create (&line_stream_cookie, NULL, 0, 0, BUFFER_BLOCK_SIZE,
+ 1, 0, 0, NULL, 0, MEM_REALLOC, MEM_FREE, O_RDWR);
+ if (err)
+ goto out;
+
+ err = es_create (&line_stream, line_stream_cookie, -1,
+ estream_functions_mem);
+ if (err)
+ goto out;
+
+ space_left = max_length;
+ line_size = 0;
+ while (1)
+ {
+ if (max_length && (space_left == 1))
+ break;
+
+ err = es_peek (stream, &data, &data_len);
+ if (err || (! data_len))
+ break;
+
+ if (data_len > (space_left - 1))
+ data_len = space_left - 1;
+
+ newline = memchr (data, '\n', data_len);
+ if (newline)
+ {
+ data_len = (newline - (char *) data) + 1;
+ err = es_write (line_stream, data, data_len, NULL);
+ if (! err)
+ {
+ space_left -= data_len;
+ line_size += data_len;
+ es_skip (stream, data_len);
+ break;
+ }
+ }
+ else
+ {
+ err = es_write (line_stream, data, data_len, NULL);
+ if (! err)
+ {
+ space_left -= data_len;
+ line_size += data_len;
+ es_skip (stream, data_len);
+ }
+ }
+ if (err)
+ break;
+ }
+ if (err)
+ goto out;
+
+ /* Complete line has been written to line_stream. */
+
+ if ((max_length > 1) && (! line_size))
+ {
+ stream->intern->indicators.eof = 1;
+ goto out;
+ }
+
+ err = es_seek (line_stream, 0, SEEK_SET, NULL);
+ if (err)
+ goto out;
+
+ if (! *line)
+ {
+ line_new = MEM_ALLOC (line_size + 1);
+ if (! line_new)
+ {
+ err = -1;
+ goto out;
+ }
+ }
+ else
+ line_new = *line;
+
+ err = es_read (line_stream, line_new, line_size, NULL);
+ if (err)
+ goto out;
+
+ line_new[line_size] = '\0';
+
+ if (! *line)
+ *line = line_new;
+ if (line_length)
+ *line_length = line_size;
+
+ out:
+
+ if (line_stream)
+ es_destroy (line_stream);
+ else if (line_stream_cookie)
+ es_func_mem_destroy (line_stream_cookie);
+
+ if (err)
+ {
+ if (! *line)
+ MEM_FREE (line_new);
+ stream->intern->indicators.err = 1;
+ }
+
+ return err;
+}
+
+
+static int
+es_print (estream_t ES__RESTRICT stream,
+ const char *ES__RESTRICT format, va_list ap)
+{
+ char data[BUFFER_BLOCK_SIZE];
+ size_t bytes_written;
+ size_t bytes_read;
+ FILE *tmp_stream;
+ int err;
+
+ bytes_written = 0;
+ tmp_stream = NULL;
+ err = 0;
+
+ tmp_stream = tmpfile ();
+ if (! tmp_stream)
+ {
+ err = errno;
+ goto out;
+ }
+
+ err = vfprintf (tmp_stream, format, ap);
+ if (err < 0)
+ goto out;
+
+ err = fseek (tmp_stream, 0, SEEK_SET);
+ if (err)
+ goto out;
+
+ while (1)
+ {
+ bytes_read = fread (data, 1, sizeof (data), tmp_stream);
+ if (ferror (tmp_stream))
+ {
+ err = -1;
+ break;
+ }
+
+ err = es_writen (stream, data, bytes_read, NULL);
+ if (err)
+ break;
+ else
+ bytes_written += bytes_read;
+ if (feof (tmp_stream))
+ break;
+ }
+ if (err)
+ goto out;
+
+ out:
+
+ if (tmp_stream)
+ fclose (tmp_stream);
+
+ return err ? -1 : bytes_written;
+}
+
+
+static void
+es_set_indicators (estream_t stream, int ind_err, int ind_eof)
+{
+ if (ind_err != -1)
+ stream->intern->indicators.err = ind_err ? 1 : 0;
+ if (ind_eof != -1)
+ stream->intern->indicators.eof = ind_eof ? 1 : 0;
+}
+
+
+static int
+es_get_indicator (estream_t stream, int ind_err, int ind_eof)
+{
+ int ret = 0;
+
+ if (ind_err)
+ ret = stream->intern->indicators.err;
+ else if (ind_eof)
+ ret = stream->intern->indicators.eof;
+
+ return ret;
+}
+
+
+static int
+es_set_buffering (estream_t ES__RESTRICT stream,
+ char *ES__RESTRICT buffer, int mode, size_t size)
+{
+ int err;
+
+ /* Flush or empty buffer depending on mode. */
+ if (stream->flags & ES_FLAG_WRITING)
+ {
+ err = es_flush (stream);
+ if (err)
+ goto out;
+ }
+ else
+ es_empty (stream);
+
+ es_set_indicators (stream, -1, 0);
+
+ /* Free old buffer in case that was allocated by this function. */
+ if (stream->intern->deallocate_buffer)
+ {
+ stream->intern->deallocate_buffer = 0;
+ MEM_FREE (stream->buffer);
+ stream->buffer = NULL;
+ }
+
+ if (mode == _IONBF)
+ stream->buffer_size = 0;
+ else
+ {
+ void *buffer_new;
+
+ if (buffer)
+ buffer_new = buffer;
+ else
+ {
+ buffer_new = MEM_ALLOC (size);
+ if (! buffer_new)
+ {
+ err = -1;
+ goto out;
+ }
+ }
+
+ stream->buffer = buffer_new;
+ stream->buffer_size = size;
+ if (! buffer)
+ stream->intern->deallocate_buffer = 1;
+ }
+ stream->intern->strategy = mode;
+ err = 0;
+
+ out:
+
+ return err;
+}
+
+
+static off_t
+es_offset_calculate (estream_t stream)
+{
+ off_t offset;
+
+ offset = stream->intern->offset + stream->data_offset;
+ if (offset < stream->unread_data_len)
+ /* Offset undefined. */
+ offset = 0;
+ else
+ offset -= stream->unread_data_len;
+
+ return offset;
+}
+
+
+static void
+es_opaque_ctrl (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque_new,
+ void **ES__RESTRICT opaque_old)
+{
+ if (opaque_old)
+ *opaque_old = stream->intern->opaque;
+ if (opaque_new)
+ stream->intern->opaque = opaque_new;
+}
+
+
+static int
+es_get_fd (estream_t stream)
+{
+ return stream->intern->fd;
+}
+
+
+
+/* API. */
+
+int
+es_init (void)
+{
+ int err;
+
+ err = es_init_do ();
+
+ return err;
+}
+
+estream_t
+es_fopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode)
+{
+ unsigned int flags;
+ int create_called;
+ estream_t stream;
+ void *cookie;
+ int err;
+ int fd;
+
+ stream = NULL;
+ cookie = NULL;
+ create_called = 0;
+
+ err = es_convert_mode (mode, &flags);
+ if (err)
+ goto out;
+
+ err = es_func_file_create (&cookie, &fd, path, flags);
+ if (err)
+ goto out;
+
+ create_called = 1;
+ err = es_create (&stream, cookie, fd, estream_functions_file);
+ if (err)
+ goto out;
+
+ out:
+
+ if (err && create_called)
+ (*estream_functions_file.func_close) (cookie);
+
+ return stream;
+}
+
+
+estream_t
+es_mopen (unsigned char *ES__RESTRICT data, size_t data_n, size_t data_len,
+ unsigned int grow,
+ func_realloc_t func_realloc, func_free_t func_free,
+ const char *ES__RESTRICT mode)
+{
+ unsigned int flags;
+ int create_called;
+ estream_t stream;
+ void *cookie;
+ int err;
+
+ cookie = 0;
+ stream = NULL;
+ create_called = 0;
+
+ err = es_convert_mode (mode, &flags);
+ if (err)
+ goto out;
+
+ err = es_func_mem_create (&cookie, data, data_n, data_len,
+ BUFFER_BLOCK_SIZE, grow, 0, 0,
+ NULL, 0, func_realloc, func_free, flags);
+ if (err)
+ goto out;
+
+ create_called = 1;
+ err = es_create (&stream, cookie, -1, estream_functions_mem);
+
+ out:
+
+ if (err && create_called)
+ (*estream_functions_mem.func_close) (cookie);
+
+ return stream;
+}
+
+
+estream_t
+es_open_memstream (char **ptr, size_t *size)
+{
+ unsigned int flags;
+ int create_called;
+ estream_t stream;
+ void *cookie;
+ int err;
+
+ flags = O_RDWR;
+ create_called = 0;
+ stream = NULL;
+ cookie = 0;
+
+ err = es_func_mem_create (&cookie, NULL, 0, 0,
+ BUFFER_BLOCK_SIZE, 1, 1, 1,
+ ptr, size, MEM_REALLOC, MEM_FREE, flags);
+ if (err)
+ goto out;
+
+ create_called = 1;
+ err = es_create (&stream, cookie, -1, estream_functions_mem);
+
+ out:
+
+ if (err && create_called)
+ (*estream_functions_mem.func_close) (cookie);
+
+ return stream;
+}
+
+
+estream_t
+es_fopencookie (void *ES__RESTRICT cookie,
+ const char *ES__RESTRICT mode,
+ es_cookie_io_functions_t functions)
+{
+ unsigned int flags;
+ estream_t stream;
+ int err;
+
+ stream = NULL;
+ flags = 0;
+
+ err = es_convert_mode (mode, &flags);
+ if (err)
+ goto out;
+
+ err = es_create (&stream, cookie, -1, functions);
+ if (err)
+ goto out;
+
+ out:
+
+ return stream;
+}
+
+
+estream_t
+es_fdopen (int filedes, const char *mode)
+{
+ unsigned int flags;
+ int create_called;
+ estream_t stream;
+ void *cookie;
+ int err;
+
+ stream = NULL;
+ cookie = NULL;
+ create_called = 0;
+
+ err = es_convert_mode (mode, &flags);
+ if (err)
+ goto out;
+
+ err = es_func_fd_create (&cookie, filedes, flags);
+ if (err)
+ goto out;
+
+ create_called = 1;
+ err = es_create (&stream, cookie, filedes, estream_functions_fd);
+
+ out:
+
+ if (err && create_called)
+ (*estream_functions_fd.func_close) (cookie);
+
+ return stream;
+}
+
+
+estream_t
+es_freopen (const char *ES__RESTRICT path, const char *ES__RESTRICT mode,
+ estream_t ES__RESTRICT stream)
+{
+ int err;
+
+ if (path)
+ {
+ unsigned int flags;
+ int create_called;
+ void *cookie;
+ int fd;
+
+ cookie = NULL;
+ create_called = 0;
+
+ ESTREAM_LOCK (stream);
+
+ es_deinitialize (stream);
+
+ err = es_convert_mode (mode, &flags);
+ if (err)
+ goto leave;
+
+ err = es_func_file_create (&cookie, &fd, path, flags);
+ if (err)
+ goto leave;
+
+ create_called = 1;
+ es_initialize (stream, cookie, fd, estream_functions_file);
+
+ leave:
+
+ if (err)
+ {
+ if (create_called)
+ es_func_fd_destroy (cookie);
+
+ es_destroy (stream);
+ stream = NULL;
+ }
+ else
+ ESTREAM_UNLOCK (stream);
+ }
+ else
+ {
+ /* FIXME? We don't support re-opening at the moment. */
+ errno = EINVAL;
+ es_deinitialize (stream);
+ es_destroy (stream);
+ stream = NULL;
+ }
+
+ return stream;
+}
+
+
+int
+es_fclose (estream_t stream)
+{
+ int err;
+
+ err = es_destroy (stream);
+
+ return err;
+}
+
+int
+es_fileno_unlocked (estream_t stream)
+{
+ return es_get_fd (stream);
+}
+
+
+void
+es_flockfile (estream_t stream)
+{
+ ESTREAM_LOCK (stream);
+}
+
+
+int
+es_ftrylockfile (estream_t stream)
+{
+ return ESTREAM_TRYLOCK (stream);
+}
+
+
+void
+es_funlockfile (estream_t stream)
+{
+ ESTREAM_UNLOCK (stream);
+}
+
+
+int
+es_fileno (estream_t stream)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_fileno_unlocked (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+int
+es_feof_unlocked (estream_t stream)
+{
+ return es_get_indicator (stream, 0, 1);
+}
+
+
+int
+es_feof (estream_t stream)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_feof_unlocked (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+int
+es_ferror_unlocked (estream_t stream)
+{
+ return es_get_indicator (stream, 1, 0);
+}
+
+
+int
+es_ferror (estream_t stream)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_ferror_unlocked (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+void
+es_clearerr_unlocked (estream_t stream)
+{
+ es_set_indicators (stream, 0, 0);
+}
+
+
+void
+es_clearerr (estream_t stream)
+{
+ ESTREAM_LOCK (stream);
+ es_clearerr_unlocked (stream);
+ ESTREAM_UNLOCK (stream);
+}
+
+
+int
+es_fflush (estream_t stream)
+{
+ int err;
+
+ if (stream)
+ {
+ ESTREAM_LOCK (stream);
+ if (stream->flags & ES_FLAG_WRITING)
+ err = es_flush (stream);
+ else
+ {
+ es_empty (stream);
+ err = 0;
+ }
+ ESTREAM_UNLOCK (stream);
+ }
+ else
+ err = es_list_iterate (es_fflush);
+
+ return err ? EOF : 0;
+}
+
+
+int
+es_fseek (estream_t stream, long int offset, int whence)
+{
+ int err;
+
+ ESTREAM_LOCK (stream);
+ err = es_seek (stream, offset, whence, NULL);
+ ESTREAM_UNLOCK (stream);
+
+ return err;
+}
+
+
+int
+es_fseeko (estream_t stream, off_t offset, int whence)
+{
+ int err;
+
+ ESTREAM_LOCK (stream);
+ err = es_seek (stream, offset, whence, NULL);
+ ESTREAM_UNLOCK (stream);
+
+ return err;
+}
+
+
+long int
+es_ftell (estream_t stream)
+{
+ long int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_offset_calculate (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+off_t
+es_ftello (estream_t stream)
+{
+ off_t ret = -1;
+
+ ESTREAM_LOCK (stream);
+ ret = es_offset_calculate (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+void
+es_rewind (estream_t stream)
+{
+ ESTREAM_LOCK (stream);
+ es_seek (stream, 0L, SEEK_SET, NULL);
+ es_set_indicators (stream, 0, -1);
+ ESTREAM_UNLOCK (stream);
+}
+
+
+int
+_es_getc_underflow (estream_t stream)
+{
+ int err;
+ unsigned char c;
+ size_t bytes_read;
+
+ err = es_readn (stream, &c, 1, &bytes_read);
+
+ return (err || (! bytes_read)) ? EOF : c;
+}
+
+
+int
+_es_putc_overflow (int c, estream_t stream)
+{
+ unsigned char d = c;
+ int err;
+
+ err = es_writen (stream, &d, 1, NULL);
+
+ return err ? EOF : c;
+}
+
+
+int
+es_fgetc (estream_t stream)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_getc_unlocked (stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+int
+es_fputc (int c, estream_t stream)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_putc_unlocked (c, stream);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+int
+es_ungetc (int c, estream_t stream)
+{
+ unsigned char data = (unsigned char) c;
+ size_t data_unread;
+
+ ESTREAM_LOCK (stream);
+ es_unreadn (stream, &data, 1, &data_unread);
+ ESTREAM_UNLOCK (stream);
+
+ return data_unread ? c : EOF;
+}
+
+
+int
+es_read (estream_t ES__RESTRICT stream,
+ void *ES__RESTRICT buffer, size_t bytes_to_read,
+ size_t *ES__RESTRICT bytes_read)
+{
+ int err;
+
+ if (bytes_to_read)
+ {
+ ESTREAM_LOCK (stream);
+ err = es_readn (stream, buffer, bytes_to_read, bytes_read);
+ ESTREAM_UNLOCK (stream);
+ }
+ else
+ err = 0;
+
+ return err;
+}
+
+
+int
+es_write (estream_t ES__RESTRICT stream,
+ const void *ES__RESTRICT buffer, size_t bytes_to_write,
+ size_t *ES__RESTRICT bytes_written)
+{
+ int err;
+
+ if (bytes_to_write)
+ {
+ ESTREAM_LOCK (stream);
+ err = es_writen (stream, buffer, bytes_to_write, bytes_written);
+ ESTREAM_UNLOCK (stream);
+ }
+ else
+ err = 0;
+
+ return err;
+}
+
+
+size_t
+es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
+ estream_t ES__RESTRICT stream)
+{
+ size_t ret, bytes;
+ int err;
+
+ if (size * nitems)
+ {
+ ESTREAM_LOCK (stream);
+ err = es_readn (stream, ptr, size * nitems, &bytes);
+ ESTREAM_UNLOCK (stream);
+
+ ret = bytes / size;
+ }
+ else
+ ret = 0;
+
+ return ret;
+}
+
+
+size_t
+es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t nitems,
+ estream_t ES__RESTRICT stream)
+{
+ size_t ret, bytes;
+ int err;
+
+ if (size * nitems)
+ {
+ ESTREAM_LOCK (stream);
+ err = es_writen (stream, ptr, size * nitems, &bytes);
+ ESTREAM_UNLOCK (stream);
+
+ ret = bytes / size;
+ }
+ else
+ ret = 0;
+
+ return ret;
+}
+
+
+char *
+es_fgets (char *ES__RESTRICT s, int n, estream_t ES__RESTRICT stream)
+{
+ char *ret = NULL;
+
+ if (n)
+ {
+ int err;
+
+ ESTREAM_LOCK (stream);
+ err = es_read_line (stream, n, &s, NULL);
+ ESTREAM_UNLOCK (stream);
+ if (! err)
+ ret = s;
+ }
+
+ return ret;
+}
+
+
+int
+es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream)
+{
+ size_t length;
+ int err;
+
+ length = strlen (s);
+ ESTREAM_LOCK (stream);
+ err = es_writen (stream, s, length, NULL);
+ ESTREAM_UNLOCK (stream);
+
+ return err ? EOF : 0;
+}
+
+
+ssize_t
+es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr, size_t *ES__RESTRICT n,
+ estream_t ES__RESTRICT stream)
+{
+ char *line = NULL;
+ size_t line_n = 0;
+ int err;
+
+ ESTREAM_LOCK (stream);
+ err = es_read_line (stream, 0, &line, &line_n);
+ ESTREAM_UNLOCK (stream);
+ if (err)
+ goto out;
+
+ if (*n)
+ {
+ /* Caller wants us to use his buffer. */
+
+ if (*n < (line_n + 1))
+ {
+ /* Provided buffer is too small -> resize. */
+
+ void *p;
+
+ p = MEM_REALLOC (*lineptr, line_n + 1);
+ if (! p)
+ err = -1;
+ else
+ {
+ if (*lineptr != p)
+ *lineptr = p;
+ }
+ }
+
+ if (! err)
+ {
+ memcpy (*lineptr, line, line_n + 1);
+ if (*n != line_n)
+ *n = line_n;
+ }
+ MEM_FREE (line);
+ }
+ else
+ {
+ /* Caller wants new buffers. */
+ *lineptr = line;
+ *n = line_n;
+ }
+
+ out:
+
+ return err ? err : line_n;
+}
+
+
+int
+es_vfprintf (estream_t ES__RESTRICT stream, const char *ES__RESTRICT format,
+ va_list ap)
+{
+ int ret;
+
+ ESTREAM_LOCK (stream);
+ ret = es_print (stream, format, ap);
+ ESTREAM_UNLOCK (stream);
+
+ return ret;
+}
+
+
+int
+es_fprintf (estream_t ES__RESTRICT stream,
+ const char *ES__RESTRICT format, ...)
+{
+ int ret;
+
+ va_list ap;
+ va_start (ap, format);
+ ESTREAM_LOCK (stream);
+ ret = es_print (stream, format, ap);
+ ESTREAM_UNLOCK (stream);
+ va_end (ap);
+
+ return ret;
+}
+
+static int
+tmpfd (void)
+{
+ FILE *fp;
+ int fp_fd;
+ int fd;
+
+ fp = NULL;
+ fd = -1;
+
+ fp = tmpfile ();
+ if (! fp)
+ goto out;
+
+ fp_fd = fileno (fp);
+ fd = dup (fp_fd);
+
+ out:
+
+ if (fp)
+ fclose (fp);
+
+ return fd;
+}
+
+estream_t
+es_tmpfile (void)
+{
+ unsigned int flags;
+ int create_called;
+ estream_t stream;
+ void *cookie;
+ int err;
+ int fd;
+
+ create_called = 0;
+ stream = NULL;
+ flags = O_RDWR | O_TRUNC | O_CREAT;
+ cookie = NULL;
+
+ fd = tmpfd ();
+ if (fd == -1)
+ {
+ err = -1;
+ goto out;
+ }
+
+ err = es_func_fd_create (&cookie, fd, flags);
+ if (err)
+ goto out;
+
+ create_called = 1;
+ err = es_create (&stream, cookie, fd, estream_functions_fd);
+
+ out:
+
+ if (err)
+ {
+ if (create_called)
+ es_func_fd_destroy (cookie);
+ else if (fd != -1)
+ close (fd);
+ stream = NULL;
+ }
+
+ return stream;
+}
+
+
+int
+es_setvbuf (estream_t ES__RESTRICT stream,
+ char *ES__RESTRICT buf, int type, size_t size)
+{
+ int err;
+
+ if (((type == _IOFBF) || (type == _IOLBF) || (type == _IONBF))
+ && (! ((! size) && (type != _IONBF))))
+ {
+ ESTREAM_LOCK (stream);
+ err = es_set_buffering (stream, buf, type, size);
+ ESTREAM_UNLOCK (stream);
+ }
+ else
+ {
+ errno = EINVAL;
+ err = -1;
+ }
+
+ return err;
+}
+
+
+void
+es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf)
+{
+ ESTREAM_LOCK (stream);
+ es_set_buffering (stream, buf, buf ? _IOFBF : _IONBF, BUFSIZ);
+ ESTREAM_UNLOCK (stream);
+}
+
+void
+es_opaque_set (estream_t stream, void *opaque)
+{
+ ESTREAM_LOCK (stream);
+ es_opaque_ctrl (stream, opaque, NULL);
+ ESTREAM_UNLOCK (stream);
+}
+
+
+void *
+es_opaque_get (estream_t stream)
+{
+ void *opaque;
+
+ ESTREAM_LOCK (stream);
+ es_opaque_ctrl (stream, NULL, &opaque);
+ ESTREAM_UNLOCK (stream);
+
+ return opaque;
+}
diff --git a/common/estream.h b/common/estream.h
new file mode 100644
index 000000000..a9b4847c8
--- /dev/null
+++ b/common/estream.h
@@ -0,0 +1,203 @@
+/* estream.h - Extended stream I/O/ Library
+ * Copyright (C) 2004 g10 Code GmbH
+ *
+ * This file is part of Libestream.
+ *
+ * Libestream is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published
+ * by the Free Software Foundation; either version 2 of the License,
+ * or (at your option) any later version.
+ *
+ * Libestream 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 General Public License
+ * along with Libestream; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef ESTREAM_H
+#define ESTREAM_H
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+
+/* Forward declaration for the (opaque) internal type. */
+struct estream_internal;
+
+/* The definition of this struct is entirely private. You must not
+ use it for anything. It is only here so some functions can be
+ implemented as macros. */
+struct es__stream
+{
+ /* The layout of this struct must never change. It may be grown,
+ but only if all functions which access the new members are
+ versioned. */
+
+ /* A pointer to the stream buffer. */
+ unsigned char *buffer;
+
+ /* The size of the buffer in bytes. */
+ size_t buffer_size;
+
+ /* The length of the usable data in the buffer, only valid when in
+ read mode (see flags). */
+ size_t data_len;
+
+ /* The current position of the offset pointer, valid in read and
+ write mode. */
+ size_t data_offset;
+
+ size_t data_flushed;
+ unsigned char *unread_buffer;
+ size_t unread_buffer_size;
+
+ /* The number of unread bytes. */
+ size_t unread_data_len;
+
+ /* Various flags. */
+#define ES__FLAG_WRITING (1 << 0)
+ unsigned int flags;
+
+ /* A pointer to our internal data for this stream. */
+ struct estream_internal *intern;
+};
+
+/* The opaque type for an estream. */
+typedef struct es__stream *estream_t;
+
+
+typedef ssize_t (*es_cookie_read_function_t) (void *cookie,
+ void *buffer, size_t size);
+typedef ssize_t (*es_cookie_write_function_t) (void *cookie,
+ const void *buffer,
+ size_t size);
+typedef int (*es_cookie_seek_function_t) (void *cookie,
+ off_t *pos, int whence);
+typedef int (*es_cookie_close_function_t) (void *cookie);
+
+typedef struct es_cookie_io_functions
+{
+ es_cookie_read_function_t func_read;
+ es_cookie_write_function_t func_write;
+ es_cookie_seek_function_t func_seek;
+ es_cookie_close_function_t func_close;
+} es_cookie_io_functions_t;
+
+
+#ifndef ES__RESTRICT
+# if defined __GNUC__ && defined __GNUC_MINOR__
+# if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 92))
+# define ES__RESTRICT __restrict__
+# endif
+# endif
+#endif
+#ifndef ES__RESTRICT
+# define ES__RESTRICT
+#endif
+
+int es_init (void);
+
+estream_t es_fopen (const char *ES__RESTRICT path,
+ const char *ES__RESTRICT mode);
+estream_t es_mopen (unsigned char *ES__RESTRICT data,
+ size_t data_n, size_t data_len,
+ unsigned int grow,
+ void *(*func_realloc) (void *mem, size_t size),
+ void (*func_free) (void *mem),
+ const char *ES__RESTRICT mode);
+estream_t es_open_memstream (char **ptr, size_t *size);
+estream_t es_fdopen (int filedes, const char *mode);
+estream_t es_freopen (const char *ES__RESTRICT path,
+ const char *ES__RESTRICT mode,
+ estream_t ES__RESTRICT stream);
+estream_t es_fopencookie (void *ES__RESTRICT cookie,
+ const char *ES__RESTRICT mode,
+ es_cookie_io_functions_t functions);
+int es_fclose (estream_t stream);
+int es_fileno (estream_t stream);
+int es_fileno_unlocked (estream_t stream);
+
+void es_flockfile (estream_t stream);
+int es_ftrylockfile (estream_t stream);
+void es_funlockfile (estream_t stream);
+
+int es_feof (estream_t stream);
+int es_feof_unlocked (estream_t stream);
+int es_ferror (estream_t stream);
+int es_ferror_unlocked (estream_t stream);
+void es_clearerr (estream_t stream);
+void es_clearerr_unlocked (estream_t stream);
+
+int es_fflush (estream_t stream);
+int es_fseek (estream_t stream, long int offset, int whence);
+int es_fseeko (estream_t stream, off_t offset, int whence);
+long int es_ftell (estream_t stream);
+off_t es_ftello (estream_t stream);
+void es_rewind (estream_t stream);
+
+int es_fgetc (estream_t stream);
+int es_fputc (int c, estream_t stream);
+
+int _es_getc_underflow (estream_t stream);
+int _es_putc_overflow (int c, estream_t stream);
+
+#define es_getc_unlocked(stream) \
+ (((! ((stream)->flags & 1)) \
+ && ((stream)->data_offset < (stream)->data_len) \
+ && (! (stream)->unread_data_len)) \
+ ? ((int) (stream)->buffer[((stream)->data_offset)++]) \
+ : _es_getc_underflow ((stream)))
+
+#define es_putc_unlocked(c, stream) \
+ ((((stream)->flags & 1) \
+ && ((stream)->data_offset < (stream)->buffer_size) \
+ && (c != '\n')) \
+ ? ((int) ((stream)->buffer[((stream)->data_offset)++] = (c))) \
+ : _es_putc_overflow ((c), (stream)))
+
+#define es_getc(stream) es_fgetc (stream)
+#define es_putc(c, stream) es_fputc (c, stream)
+
+int es_ungetc (int c, estream_t stream);
+
+int es_read (estream_t ES__RESTRICT stream,
+ void *ES__RESTRICT buffer, size_t bytes_to_read,
+ size_t *ES__RESTRICT bytes_read);
+int es_write (estream_t ES__RESTRICT stream,
+ const void *ES__RESTRICT buffer, size_t bytes_to_write,
+ size_t *ES__RESTRICT bytes_written);
+
+size_t es_fread (void *ES__RESTRICT ptr, size_t size, size_t nitems,
+ estream_t ES__RESTRICT stream);
+size_t es_fwrite (const void *ES__RESTRICT ptr, size_t size, size_t memb,
+ estream_t ES__RESTRICT stream);
+
+char *es_fgets (char *ES__RESTRICT s, int n, estream_t ES__RESTRICT stream);
+int es_fputs (const char *ES__RESTRICT s, estream_t ES__RESTRICT stream);
+
+ssize_t es_getline (char *ES__RESTRICT *ES__RESTRICT lineptr,
+ size_t *ES__RESTRICT n,
+ estream_t stream);
+
+int es_fprintf (estream_t ES__RESTRICT stream,
+ const char *ES__RESTRICT format, ...);
+int es_vfprintf (estream_t ES__RESTRICT stream,
+ const char *ES__RESTRICT format, va_list ap);
+
+int es_setvbuf (estream_t ES__RESTRICT stream,
+ char *ES__RESTRICT buf, int mode, size_t size);
+void es_setbuf (estream_t ES__RESTRICT stream, char *ES__RESTRICT buf);
+
+estream_t es_tmpfile (void);
+
+void es_opaque_set (estream_t ES__RESTRICT stream, void *ES__RESTRICT opaque);
+void *es_opaque_get (estream_t stream);
+
+#endif /*ESTREAM_H*/
+
diff --git a/common/exechelp.c b/common/exechelp.c
new file mode 100644
index 000000000..e64b69022
--- /dev/null
+++ b/common/exechelp.c
@@ -0,0 +1,483 @@
+/* exechelp.c - fork and exec helpers
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+#ifdef USE_GNU_PTH
+#include <pth.h>
+#endif
+#ifndef HAVE_W32_SYSTEM
+#include <sys/wait.h>
+#endif
+
+#include "util.h"
+#include "i18n.h"
+#include "exechelp.h"
+
+/* Define to 1 do enable debugging. */
+#define DEBUG_W32_SPAWN 1
+
+
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+/* We have the usual problem here: Some modules are linked against pth
+ and some are not. However we want to use pth_fork and pth_waitpid
+ here. Using a weak symbol works but is not portable - we should
+ provide a an explicit dummy pth module instead of using the
+ pragma. */
+#ifndef _WIN32
+#pragma weak pth_fork
+#pragma weak pth_waitpid
+#endif
+
+
+#ifdef HAVE_W32_SYSTEM
+/* We assume that a HANDLE can be represented by an int which should
+ be true for all i386 systems (HANDLE is defined as void *) and
+ these are the only systems for which Windows is available. Further
+ we assume that -1 denotes an invalid handle. */
+# define fd_to_handle(a) ((HANDLE)(a))
+# define handle_to_fd(a) ((int)(a))
+# define pid_to_handle(a) ((HANDLE)(a))
+# define handle_to_pid(a) ((int)(a))
+#endif
+
+
+#ifdef HAVE_W32_SYSTEM
+/* Build a command line for use with W32's CreateProcess. On success
+ CMDLINE gets the address of a newly allocated string. */
+static gpg_error_t
+build_w32_commandline (const char *pgmname, const char **argv, char **cmdline)
+{
+ int i, n;
+ const char *s;
+ char *buf, *p;
+
+ *cmdline = NULL;
+ n = strlen (pgmname);
+ for (i=0; (s=argv[i]); i++)
+ {
+ n += strlen (s) + 1 + 2; /* (1 space, 2 quoting */
+ for (; *s; s++)
+ if (*s == '\"')
+ n++; /* Need to double inner quotes. */
+ }
+ n++;
+
+ buf = p = xtrymalloc (n);
+ if (!buf)
+ return gpg_error_from_errno (errno);
+
+ /* fixme: PGMNAME may not contain spaces etc. */
+ p = stpcpy (p, pgmname);
+ for (i=0; argv[i]; i++)
+ {
+ if (!*argv[i]) /* Empty string. */
+ p = stpcpy (p, " \"\"");
+ else if (strpbrk (argv[i], " \t\n\v\f\""))
+ {
+ p = stpcpy (p, " \"");
+ for (s=argv[i]; *s; s++)
+ {
+ *p++ = *s;
+ if (*s == '\"')
+ *p++ = *s;
+ }
+ *p++ = '\"';
+ *p = 0;
+ }
+ else
+ p = stpcpy (stpcpy (p, " "), argv[i]);
+ }
+
+ *cmdline= buf;
+ return 0;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+#ifdef HAVE_W32_SYSTEM
+/* Create pipe where the write end is inheritable. */
+static int
+create_inheritable_pipe (int filedes[2])
+{
+ HANDLE r, w, h;
+ SECURITY_ATTRIBUTES sec_attr;
+
+ memset (&sec_attr, 0, sizeof sec_attr );
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ if (!CreatePipe (&r, &w, &sec_attr, 0))
+ return -1;
+
+ if (!DuplicateHandle (GetCurrentProcess(), w,
+ GetCurrentProcess(), &h, 0,
+ TRUE, DUPLICATE_SAME_ACCESS ))
+ {
+ log_error ("DuplicateHandle failed: %s\n", w32_strerror (-1));
+ CloseHandle (r);
+ CloseHandle (w);
+ return -1;
+ }
+ CloseHandle (w);
+ w = h;
+
+ filedes[0] = handle_to_fd (r);
+ filedes[1] = handle_to_fd (w);
+ return 0;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+/* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
+ stdin, write the output to OUTFILE, return a new stream in
+ STATUSFILE for stderr and the pid of the process in PID. The
+ arguments for the process are expected in the NULL terminated array
+ ARGV. The program name itself should not be included there. if
+ PREEXEC is not NULL, that function will be called right before the
+ exec.
+
+ Returns 0 on success or an error code. */
+gpg_error_t
+gnupg_spawn_process (const char *pgmname, const char *argv[],
+ FILE *infile, FILE *outfile,
+ void (*preexec)(void),
+ FILE **statusfile, pid_t *pid)
+{
+#ifdef HAVE_W32_SYSTEM
+ gpg_error_t err;
+ SECURITY_ATTRIBUTES sec_attr;
+ PROCESS_INFORMATION pi =
+ {
+ NULL, /* Returns process handle. */
+ 0, /* Returns primary thread handle. */
+ 0, /* Returns pid. */
+ 0 /* Returns tid. */
+ };
+ STARTUPINFO si;
+ int cr_flags;
+ char *cmdline;
+ int fd, fdout, rp[2];
+
+ /* Setup return values. */
+ *statusfile = NULL;
+ *pid = (pid_t)(-1);
+ fflush (infile);
+ rewind (infile);
+ fd = _get_osfhandle (fileno (infile));
+ fdout = _get_osfhandle (fileno (outfile));
+ if (fd == -1 || fdout == -1)
+ log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
+
+ /* Prepare security attributes. */
+ memset (&sec_attr, 0, sizeof sec_attr );
+ sec_attr.nLength = sizeof sec_attr;
+ sec_attr.bInheritHandle = FALSE;
+
+ /* Build the command line. */
+ err = build_w32_commandline (pgmname, argv, &cmdline);
+ if (err)
+ return err;
+
+ /* Create a pipe. */
+ if (create_inheritable_pipe (rp))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+ xfree (cmdline);
+ return err;
+ }
+
+ /* Start the process. Note that we can't run the PREEXEC function
+ because this would change our own environment. */
+ memset (&si, 0, sizeof si);
+ si.cb = sizeof (si);
+ si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ si.wShowWindow = DEBUG_W32_SPAWN? SW_SHOW : SW_MINIMIZE;
+ si.hStdInput = fd_to_handle (fd);
+ si.hStdOutput = fd_to_handle (fdout);
+ si.hStdError = fd_to_handle (rp[1]);
+
+ cr_flags = (CREATE_DEFAULT_ERROR_MODE
+ | GetPriorityClass (GetCurrentProcess ())
+ | CREATE_SUSPENDED);
+ log_debug ("CreateProcess, path=`%s' cmdline=`%s'\n", pgmname, cmdline);
+ if (!CreateProcess (pgmname, /* Program to start. */
+ cmdline, /* Command line arguments. */
+ &sec_attr, /* Process security attributes. */
+ &sec_attr, /* Thread security attributes. */
+ TRUE, /* Inherit handles. */
+ cr_flags, /* Creation flags. */
+ NULL, /* Environment. */
+ NULL, /* Use current drive/directory. */
+ &si, /* Startup information. */
+ &pi /* Returns process information. */
+ ))
+ {
+ log_error ("CreateProcess failed: %s\n", w32_strerror (-1));
+ xfree (cmdline);
+ CloseHandle (fd_to_handle (rp[0]));
+ CloseHandle (fd_to_handle (rp[1]));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ xfree (cmdline);
+ cmdline = NULL;
+
+ /* Close the other end of the pipe. */
+ CloseHandle (fd_to_handle (rp[1]));
+
+ log_debug ("CreateProcess ready: hProcess=%p hThread=%p"
+ " dwProcessID=%d dwThreadId=%d\n",
+ pi.hProcess, pi.hThread,
+ (int) pi.dwProcessId, (int) pi.dwThreadId);
+
+ /* Process ha been created suspended; resume it now. */
+ ResumeThread (pi.hThread);
+ CloseHandle (pi.hThread);
+
+ {
+ int x;
+
+ x = _open_osfhandle (rp[0], 0);
+ if (x == -1)
+ log_error ("failed to translate osfhandle %p\n", (void*)rp[0] );
+ else
+ {
+ log_debug ("_open_osfhandle %p yields %d\n", (void*)fd, x );
+ *statusfile = fdopen (x, "r");
+ }
+ }
+ if (!*statusfile)
+ {
+ err = gpg_error_from_errno (errno);
+ log_error (_("can't fdopen pipe for reading: %s\n"), gpg_strerror (err));
+ CloseHandle (pi.hProcess);
+ return err;
+ }
+
+ *pid = handle_to_pid (pi.hProcess);
+ return 0;
+
+#else /* !HAVE_W32_SYSTEM */
+ gpg_error_t err;
+ int fd, fdout, rp[2];
+
+ *statusfile = NULL;
+ *pid = (pid_t)(-1);
+ fflush (infile);
+ rewind (infile);
+ fd = fileno (infile);
+ fdout = fileno (outfile);
+ if (fd == -1 || fdout == -1)
+ log_fatal ("no file descriptor for file passed to gnupg_spawn_process\n");
+
+ if (pipe (rp) == -1)
+ {
+ err = gpg_error_from_errno (errno);
+ log_error (_("error creating a pipe: %s\n"), strerror (errno));
+ return err;
+ }
+
+#ifdef USE_GNU_PTH
+ *pid = pth_fork? pth_fork () : fork ();
+#else
+ *pid = fork ();
+#endif
+ if (*pid == (pid_t)(-1))
+ {
+ err = gpg_error_from_errno (errno);
+ log_error (_("error forking process: %s\n"), strerror (errno));
+ close (rp[0]);
+ close (rp[1]);
+ return err;
+ }
+
+ if (!*pid)
+ {
+ /* Child. */
+ char **arg_list;
+ int n, i, j;
+
+ /* Create the command line argument array. */
+ for (i=0; argv[i]; i++)
+ ;
+ arg_list = xcalloc (i+2, sizeof *arg_list);
+ arg_list[0] = strrchr (pgmname, '/');
+ if (arg_list[0])
+ arg_list[0]++;
+ else
+ arg_list[0] = xstrdup (pgmname);
+ for (i=0,j=1; argv[i]; i++, j++)
+ arg_list[j] = (char*)argv[i];
+
+ /* Connect the infile to stdin. */
+ if (fd != 0 && dup2 (fd, 0) == -1)
+ log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
+
+ /* Connect the outfile to stdout. */
+ if (fdout != 1 && dup2 (fdout, 1) == -1)
+ log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+
+ /* Connect stderr to our pipe. */
+ if (rp[1] != 2 && dup2 (rp[1], 2) == -1)
+ log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
+
+ /* Close all other files. */
+ n = sysconf (_SC_OPEN_MAX);
+ if (n < 0)
+ n = MAX_OPEN_FDS;
+ for (i=3; i < n; i++)
+ close(i);
+ errno = 0;
+
+ if (preexec)
+ preexec ();
+ execv (pgmname, arg_list);
+ /* No way to print anything, as we have closed all streams. */
+ _exit (127);
+ }
+
+ /* Parent. */
+ close (rp[1]);
+
+ *statusfile = fdopen (rp[0], "r");
+ if (!*statusfile)
+ {
+ err = gpg_error_from_errno (errno);
+ log_error (_("can't fdopen pipe for reading: %s\n"), strerror (errno));
+ kill (*pid, SIGTERM);
+ *pid = (pid_t)(-1);
+ return err;
+ }
+
+ return 0;
+#endif /* !HAVE_W32_SYSTEM */
+}
+
+
+/* Wait for the process identified by PID to terminate. PGMNAME should
+ be the same as suplieed to the spawn fucntion and is only used for
+ diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
+ any failures of the spawned program or other error codes.*/
+gpg_error_t
+gnupg_wait_process (const char *pgmname, pid_t pid)
+{
+ gpg_err_code_t ec;
+
+#ifdef HAVE_W32_SYSTEM
+ HANDLE proc = fd_to_handle (pid);
+ int code;
+ DWORD exc;
+
+ if (pid == (pid_t)(-1))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* FIXME: We should do a pth_waitpid here. However this has not yet
+ been implemented. A special W32 pth system call would even be
+ better. */
+ code = WaitForSingleObject (proc, INFINITE);
+ switch (code)
+ {
+ case WAIT_FAILED:
+ log_error (_("waiting for process %d to terminate failed: %s\n"),
+ (int)pid, w32_strerror (-1));
+ ec = GPG_ERR_GENERAL;
+ break;
+
+ case WAIT_OBJECT_0:
+ if (!GetExitCodeProcess (proc, &exc))
+ {
+ log_error (_("error getting exit code of process %d: %s\n"),
+ (int)pid, w32_strerror (-1) );
+ ec = GPG_ERR_GENERAL;
+ }
+ else if (exc)
+ {
+ log_error (_("error running `%s': exit status %d\n"),
+ pgmname, (int)exc );
+ ec = GPG_ERR_GENERAL;
+ }
+ else
+ ec = 0;
+ break;
+
+ default:
+ log_error ("WaitForSingleObject returned unexpected "
+ "code %d for pid %d\n", code, (int)pid );
+ ec = GPG_ERR_GENERAL;
+ break;
+ }
+
+#else /* !HAVE_W32_SYSTEM */
+ int i, status;
+
+ if (pid == (pid_t)(-1))
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+#ifdef USE_GNU_PTH
+ i = pth_waitpid ? pth_waitpid (pid, &status, 0) : waitpid (pid, &status, 0);
+#else
+ while ( (i=waitpid (pid, &status, 0)) == -1 && errno == EINTR)
+ ;
+#endif
+ if (i == (pid_t)(-1))
+ {
+ log_error (_("waiting for process %d to terminate failed: %s\n"),
+ (int)pid, strerror (errno));
+ ec = gpg_err_code_from_errno (errno);
+ }
+ else if (WIFEXITED (status) && WEXITSTATUS (status) == 127)
+ {
+ log_error (_("error running `%s': probably not installed\n"), pgmname);
+ ec = GPG_ERR_CONFIGURATION;
+ }
+ else if (WIFEXITED (status) && WEXITSTATUS (status))
+ {
+ log_error (_("error running `%s': exit status %d\n"), pgmname,
+ WEXITSTATUS (status));
+ ec = GPG_ERR_GENERAL;
+ }
+ else if (!WIFEXITED (status))
+ {
+ log_error (_("error running `%s': terminated\n"), pgmname);
+ ec = GPG_ERR_GENERAL;
+ }
+ else
+ ec = 0;
+#endif /* !HAVE_W32_SYSTEM */
+
+ return gpg_err_make (GPG_ERR_SOURCE_DEFAULT, ec);
+
+}
+
diff --git a/common/exechelp.h b/common/exechelp.h
new file mode 100644
index 000000000..1df029b7e
--- /dev/null
+++ b/common/exechelp.h
@@ -0,0 +1,46 @@
+/* exechelp.h - Definitions for the fork and exec helpers
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_EXECHELP_H
+#define GNUPG_COMMON_EXECHELP_H
+
+
+
+/* Fork and exec the PGMNAME, connect the file descriptor of INFILE to
+ stdin, write the output to OUTFILE, return a new stream in
+ STATUSFILE for stderr and the pid of the process in PID. The
+ arguments for the process are expected in the NULL terminated array
+ ARGV. The program name itself should not be included there. if
+ PREEXEC is not NULL, that function will be called right before the
+ exec. Returns 0 on success or an error code. */
+gpg_error_t gnupg_spawn_process (const char *pgmname, const char *argv[],
+ FILE *infile, FILE *outfile,
+ void (*preexec)(void),
+ FILE **statusfile, pid_t *pid);
+
+/* Wait for the process identified by PID to terminate. PGMNAME should
+ be the same as suplieed to the spawn fucntion and is only used for
+ diagnostics. Returns 0 if the process succeded, GPG_ERR_GENERAL for
+ any failures of the spawned program or other error codes.*/
+gpg_error_t gnupg_wait_process (const char *pgmname, pid_t pid);
+
+
+#endif /*GNUPG_COMMON_EXECHELP_H*/
diff --git a/common/gettime.c b/common/gettime.c
new file mode 100644
index 000000000..c4ea3283a
--- /dev/null
+++ b/common/gettime.c
@@ -0,0 +1,305 @@
+/* gettime.c - Wrapper for time functions
+ * Copyright (C) 1998, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <time.h>
+#ifdef HAVE_LANGINFO_H
+#include <langinfo.h>
+#endif
+
+#include "util.h"
+
+static unsigned long timewarp;
+static enum { NORMAL = 0, FROZEN, FUTURE, PAST } timemode;
+
+/* Wrapper for the time(3). We use this here so we can fake the time
+ for tests */
+time_t
+gnupg_get_time ()
+{
+ time_t current = time (NULL);
+ if (timemode == NORMAL)
+ return current;
+ else if (timemode == FROZEN)
+ return timewarp;
+ else if (timemode == FUTURE)
+ return current + timewarp;
+ else
+ return current - timewarp;
+}
+
+
+/* Return the current time (possibly faked) in ISO format. */
+void
+gnupg_get_isotime (gnupg_isotime_t timebuf)
+{
+ time_t atime = gnupg_get_time ();
+
+ if (atime < 0)
+ *timebuf = 0;
+ else
+ {
+ struct tm *tp;
+#ifdef HAVE_GMTIME_R
+ struct tm tmbuf;
+
+ tp = gmtime_r (&atime, &tmbuf);
+#else
+ tp = gmtime (&atime);
+#endif
+ sprintf (timebuf,"%04d%02d%02dT%02d%02d%02d",
+ 1900 + tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ }
+}
+
+
+/* set the time to NEWTIME so that gnupg_get_time returns a time
+ starting with this one. With FREEZE set to 1 the returned time
+ will never change. Just for completeness, a value of (time_t)-1
+ for NEWTIME gets you back to rality. Note that this is obviously
+ not thread-safe but this is not required. */
+void
+gnupg_set_time (time_t newtime, int freeze)
+{
+ time_t current = time (NULL);
+
+ if ( newtime == (time_t)-1 || current == newtime)
+ {
+ timemode = NORMAL;
+ timewarp = 0;
+ }
+ else if (freeze)
+ {
+ timemode = FROZEN;
+ timewarp = current;
+ }
+ else if (newtime > current)
+ {
+ timemode = FUTURE;
+ timewarp = newtime - current;
+ }
+ else
+ {
+ timemode = PAST;
+ timewarp = current - newtime;
+ }
+}
+
+/* Returns true when we are in timewarp mode */
+int
+gnupg_faked_time_p (void)
+{
+ return timemode;
+}
+
+
+/* This function is used by gpg because OpenPGP defines the timestamp
+ as an unsigned 32 bit value. */
+u32
+make_timestamp (void)
+{
+ time_t t = gnupg_get_time ();
+
+ if (t == (time_t)-1)
+ log_fatal ("gnupg_get_time() failed\n");
+ return (u32)t;
+}
+
+
+
+/****************
+ * Scan a date string and return a timestamp.
+ * The only supported format is "yyyy-mm-dd"
+ * Returns 0 for an invalid date.
+ */
+u32
+scan_isodatestr( const char *string )
+{
+ int year, month, day;
+ struct tm tmbuf;
+ time_t stamp;
+ int i;
+
+ if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
+ return 0;
+ for( i=0; i < 4; i++ )
+ if( !digitp (string+i) )
+ return 0;
+ if( !digitp (string+5) || !digitp(string+6) )
+ return 0;
+ if( !digitp(string+8) || !digitp(string+9) )
+ return 0;
+ year = atoi(string);
+ month = atoi(string+5);
+ day = atoi(string+8);
+ /* some basic checks */
+ if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
+ return 0;
+ memset( &tmbuf, 0, sizeof tmbuf );
+ tmbuf.tm_mday = day;
+ tmbuf.tm_mon = month-1;
+ tmbuf.tm_year = year - 1900;
+ tmbuf.tm_isdst = -1;
+ stamp = mktime( &tmbuf );
+ if( stamp == (time_t)-1 )
+ return 0;
+ return stamp;
+}
+
+
+u32
+add_days_to_timestamp( u32 stamp, u16 days )
+{
+ return stamp + days*86400L;
+}
+
+
+/****************
+ * Return a string with a time value in the form: x Y, n D, n H
+ */
+
+const char *
+strtimevalue( u32 value )
+{
+ static char buffer[30];
+ unsigned int years, days, hours, minutes;
+
+ value /= 60;
+ minutes = value % 60;
+ value /= 60;
+ hours = value % 24;
+ value /= 24;
+ days = value % 365;
+ value /= 365;
+ years = value;
+
+ sprintf(buffer,"%uy%ud%uh%um", years, days, hours, minutes );
+ if( years )
+ return buffer;
+ if( days )
+ return strchr( buffer, 'y' ) + 1;
+ return strchr( buffer, 'd' ) + 1;
+}
+
+
+/*
+ * Note: this function returns GMT
+ */
+const char *
+strtimestamp( u32 stamp )
+{
+ static char buffer[11+5];
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0) {
+ strcpy (buffer, "????" "-??" "-??");
+ }
+ else {
+ tp = gmtime( &atime );
+ sprintf(buffer,"%04d-%02d-%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
+ }
+ return buffer;
+}
+
+
+/*
+ * Note: this function returns GMT
+ */
+const char *
+isotimestamp (u32 stamp)
+{
+ static char buffer[25+5];
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0)
+ {
+ strcpy (buffer, "????" "-??" "-??" " " "??" ":" "??" ":" "??");
+ }
+ else
+ {
+ tp = gmtime ( &atime );
+ sprintf (buffer,"%04d-%02d-%02d %02d:%02d:%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ }
+ return buffer;
+}
+
+
+/****************
+ * Note: this function returns local time
+ */
+const char *
+asctimestamp( u32 stamp )
+{
+ static char buffer[50];
+#if defined (HAVE_STRFTIME) && defined (HAVE_NL_LANGINFO)
+ static char fmt[50];
+#endif
+ struct tm *tp;
+ time_t atime = stamp;
+
+ if (atime < 0) {
+ strcpy (buffer, "????" "-??" "-??");
+ return buffer;
+ }
+
+ tp = localtime( &atime );
+#ifdef HAVE_STRFTIME
+#if defined(HAVE_NL_LANGINFO)
+ mem2str( fmt, nl_langinfo(D_T_FMT), DIM(fmt)-3 );
+ if( strstr( fmt, "%Z" ) == NULL )
+ strcat( fmt, " %Z");
+ /* NOTE: gcc -Wformat-noliteral will complain here. I have
+ found no way to suppress this warning .*/
+ strftime (buffer, DIM(buffer)-1, fmt, tp);
+#else
+ /* FIXME: we should check whether the locale appends a " %Z"
+ * These locales from glibc don't put the " %Z":
+ * fi_FI hr_HR ja_JP lt_LT lv_LV POSIX ru_RU ru_SU sv_FI sv_SE zh_CN
+ */
+ strftime( buffer, DIM(buffer)-1, "%c %Z", tp );
+#endif
+ buffer[DIM(buffer)-1] = 0;
+#else
+ mem2str( buffer, asctime(tp), DIM(buffer) );
+#endif
+ return buffer;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/common/homedir.c b/common/homedir.c
new file mode 100644
index 000000000..39d6dce20
--- /dev/null
+++ b/common/homedir.c
@@ -0,0 +1,126 @@
+/* homedir.c - Setup the home directory.
+ * Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef HAVE_W32_SYSTEM
+#include <shlobj.h>
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#define CSIDL_LOCAL_APPDATA 0x001c
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#define CSIDL_FLAG_CREATE 0x8000
+#endif
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+#include "util.h"
+#include "sysutils.h"
+
+
+/* This is a helper function to load a Windows function from either of
+ one DLLs. */
+#ifdef HAVE_W32_SYSTEM
+static HRESULT
+w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
+{
+ static int initialized;
+ static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
+
+ if (!initialized)
+ {
+ static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
+ void *handle;
+ int i;
+
+ initialized = 1;
+
+ for (i=0, handle = NULL; !handle && dllnames[i]; i++)
+ {
+ handle = dlopen (dllnames[i], RTLD_LAZY);
+ if (handle)
+ {
+ func = dlsym (handle, "SHGetFolderPathA");
+ if (!func)
+ {
+ dlclose (handle);
+ handle = NULL;
+ }
+ }
+ }
+ }
+
+ if (func)
+ return func (a,b,c,d,e);
+ else
+ return -1;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Set up the default home directory. The usual --homedir option
+ should be parsed later. */
+const char *
+default_homedir (void)
+{
+ const char *dir;
+
+ dir = getenv("GNUPGHOME");
+#ifdef HAVE_W32_SYSTEM
+ if (!dir || !*dir)
+ dir = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", "HomeDir");
+ if (!dir || !*dir)
+ {
+ char path[MAX_PATH];
+
+ /* It might be better to use LOCAL_APPDATA because this is
+ defined as "non roaming" and thus more likely to be kept
+ locally. For private keys this is desired. However, given
+ that many users copy private keys anyway forth and back,
+ using a system roaming services might be better than to let
+ them do it manually. A security conscious user will anyway
+ use the registry entry to have better control. */
+ if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
+ NULL, 0, path) >= 0)
+ {
+ char *tmp = xmalloc (strlen (path) + 6 +1);
+ strcpy (stpcpy (tmp, path), "\\gnupg");
+ dir = tmp;
+
+ /* Try to create the directory if it does not yet
+ exists. */
+ if (access (dir, F_OK))
+ CreateDirectory (dir, NULL);
+ }
+ }
+#endif /*HAVE_W32_SYSTEM*/
+ if (!dir || !*dir)
+ dir = GNUPG_DEFAULT_HOMEDIR;
+
+ return dir;
+}
diff --git a/common/i18n.h b/common/i18n.h
new file mode 100644
index 000000000..0187ba265
--- /dev/null
+++ b/common/i18n.h
@@ -0,0 +1,48 @@
+/* i18n.h
+ * Copyright (C) 1998, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_I18N_H
+#define GNUPG_COMMON_I18N_H
+
+#ifdef USE_SIMPLE_GETTEXT
+ int set_gettext_file( const char *filename );
+ const char *gettext( const char *msgid );
+# define _(a) gettext (a)
+# define N_(a) (a)
+#else
+# ifdef HAVE_LOCALE_H
+# include <locale.h>
+# endif
+# ifdef ENABLE_NLS
+# include <libintl.h>
+# define _(a) gettext (a)
+# ifdef gettext_noop
+# define N_(a) gettext_noop (a)
+# else
+# define N_(a) (a)
+# endif
+# else
+# define _(a) (a)
+# define N_(a) (a)
+# endif
+#endif /*!USE_SIMPLE_GETTEXT*/
+
+#endif /*GNUPG_COMMON_I18N_H*/
diff --git a/common/iobuf.c b/common/iobuf.c
new file mode 100644
index 000000000..8f7374f8c
--- /dev/null
+++ b/common/iobuf.c
@@ -0,0 +1,2467 @@
+/* iobuf.c - file handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef HAVE_DOSISH_SYSTEM
+#include <windows.h>
+#endif
+#ifdef __riscos__
+#include <kernel.h>
+#include <swis.h>
+#endif /* __riscos__ */
+
+#include "memory.h"
+#include "util.h"
+#include "iobuf.h"
+
+/* The size of the internal buffers.
+ NOTE: If you change this value you MUST also adjust the regression
+ test "armored_key_8192" in armor.test! */
+#define IOBUF_BUFFER_SIZE 8192
+
+#undef FILE_FILTER_USES_STDIO
+
+#ifdef HAVE_DOSISH_SYSTEM
+#define USE_SETMODE 1
+#endif
+
+#ifdef FILE_FILTER_USES_STDIO
+#define my_fileno(a) fileno ((a))
+#define my_fopen_ro(a,b) fopen ((a),(b))
+#define my_fopen(a,b) fopen ((a),(b))
+typedef FILE *FILEP_OR_FD;
+#define INVALID_FP NULL
+#define FILEP_OR_FD_FOR_STDIN (stdin)
+#define FILEP_OR_FD_FOR_STDOUT (stdout)
+typedef struct
+{
+ FILE *fp; /* open file handle */
+ int keep_open;
+ int no_cache;
+ int print_only_name; /* flags indicating that fname is not a real file */
+ char fname[1]; /* name of the file */
+}
+file_filter_ctx_t;
+#else
+#define my_fileno(a) (a)
+#define my_fopen_ro(a,b) fd_cache_open ((a),(b))
+#define my_fopen(a,b) direct_open ((a),(b))
+#ifdef HAVE_DOSISH_SYSTEM
+typedef HANDLE FILEP_OR_FD;
+#define INVALID_FP ((HANDLE)-1)
+#define FILEP_OR_FD_FOR_STDIN (GetStdHandle (STD_INPUT_HANDLE))
+#define FILEP_OR_FD_FOR_STDOUT (GetStdHandle (STD_OUTPUT_HANDLE))
+#undef USE_SETMODE
+#else
+typedef int FILEP_OR_FD;
+#define INVALID_FP (-1)
+#define FILEP_OR_FD_FOR_STDIN (0)
+#define FILEP_OR_FD_FOR_STDOUT (1)
+#endif
+typedef struct
+{
+ FILEP_OR_FD fp; /* open file handle */
+ int keep_open;
+ int no_cache;
+ int eof_seen;
+ int print_only_name; /* flags indicating that fname is not a real file */
+ char fname[1]; /* name of the file */
+}
+file_filter_ctx_t;
+
+struct close_cache_s
+{
+ struct close_cache_s *next;
+ FILEP_OR_FD fp;
+ char fname[1];
+};
+typedef struct close_cache_s *CLOSE_CACHE;
+static CLOSE_CACHE close_cache;
+#endif
+
+#ifdef _WIN32
+typedef struct
+{
+ int sock;
+ int keep_open;
+ int no_cache;
+ int eof_seen;
+ int print_only_name; /* flags indicating that fname is not a real file */
+ char fname[1]; /* name of the file */
+}
+sock_filter_ctx_t;
+#endif /*_WIN32*/
+
+/* The first partial length header block must be of size 512
+ * to make it easier (and efficienter) we use a min. block size of 512
+ * for all chunks (but the last one) */
+#define OP_MIN_PARTIAL_CHUNK 512
+#define OP_MIN_PARTIAL_CHUNK_2POW 9
+
+typedef struct
+{
+ int use;
+ size_t size;
+ size_t count;
+ int partial; /* 1 = partial header, 2 in last partial packet */
+ char *buffer; /* used for partial header */
+ size_t buflen; /* used size of buffer */
+ int first_c; /* of partial header (which is > 0) */
+ int eof;
+}
+block_filter_ctx_t;
+
+static int special_names_enabled;
+
+static int underflow (iobuf_t a);
+static int translate_file_handle (int fd, int for_write);
+
+#ifndef FILE_FILTER_USES_STDIO
+
+/*
+ * Invalidate (i.e. close) a cached iobuf
+ */
+static void
+fd_cache_invalidate (const char *fname)
+{
+ CLOSE_CACHE cc;
+
+ assert (fname);
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_invalidate (%s)\n", fname);
+
+ for (cc = close_cache; cc; cc = cc->next)
+ {
+ if (cc->fp != INVALID_FP && !strcmp (cc->fname, fname))
+ {
+ if (DBG_IOBUF)
+ log_debug (" did (%s)\n", cc->fname);
+#ifdef HAVE_DOSISH_SYSTEM
+ CloseHandle (cc->fp);
+#else
+ close (cc->fp);
+#endif
+ cc->fp = INVALID_FP;
+ }
+ }
+}
+
+
+
+static FILEP_OR_FD
+direct_open (const char *fname, const char *mode)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ unsigned long da, cd, sm;
+ HANDLE hfile;
+
+ /* Note, that we do not handle all mode combinations */
+
+ /* According to the ReactOS source it seems that open() of the
+ * standard MSW32 crt does open the file in share mode which is
+ * something new for MS applications ;-)
+ */
+ if (strchr (mode, '+'))
+ {
+ fd_cache_invalidate (fname);
+ da = GENERIC_READ | GENERIC_WRITE;
+ cd = OPEN_EXISTING;
+ sm = FILE_SHARE_READ | FILE_SHARE_WRITE;
+ }
+ else if (strchr (mode, 'w'))
+ {
+ fd_cache_invalidate (fname);
+ da = GENERIC_WRITE;
+ cd = CREATE_ALWAYS;
+ sm = FILE_SHARE_WRITE;
+ }
+ else
+ {
+ da = GENERIC_READ;
+ cd = OPEN_EXISTING;
+ sm = FILE_SHARE_READ;
+ }
+
+ hfile = CreateFile (fname, da, sm, NULL, cd, FILE_ATTRIBUTE_NORMAL, NULL);
+ return hfile;
+#else
+ int oflag;
+ int cflag = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
+ /* Note, that we do not handle all mode combinations */
+ if (strchr (mode, '+'))
+ {
+ fd_cache_invalidate (fname);
+ oflag = O_RDWR;
+ }
+ else if (strchr (mode, 'w'))
+ {
+ fd_cache_invalidate (fname);
+ oflag = O_WRONLY | O_CREAT | O_TRUNC;
+ }
+ else
+ {
+ oflag = O_RDONLY;
+ }
+#ifdef O_BINARY
+ if (strchr (mode, 'b'))
+ oflag |= O_BINARY;
+#endif
+#ifndef __riscos__
+ return open (fname, oflag, cflag);
+#else
+ {
+ struct stat buf;
+ int rc = stat (fname, &buf);
+
+ /* Don't allow iobufs on directories */
+ if (!rc && S_ISDIR (buf.st_mode) && !S_ISREG (buf.st_mode))
+ return __set_errno (EISDIR);
+ else
+ return open (fname, oflag, cflag);
+ }
+#endif
+#endif
+}
+
+
+/*
+ * Instead of closing an FD we keep it open and cache it for later reuse
+ * Note that this caching strategy only works if the process does not chdir.
+ */
+static void
+fd_cache_close (const char *fname, FILEP_OR_FD fp)
+{
+ CLOSE_CACHE cc;
+
+ assert (fp);
+ if (!fname || !*fname)
+ {
+#ifdef HAVE_DOSISH_SYSTEM
+ CloseHandle (fp);
+#else
+ close (fp);
+#endif
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_close (%p) real\n", (void *) fp);
+ return;
+ }
+ /* try to reuse a slot */
+ for (cc = close_cache; cc; cc = cc->next)
+ {
+ if (cc->fp == INVALID_FP && !strcmp (cc->fname, fname))
+ {
+ cc->fp = fp;
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_close (%s) used existing slot\n", fname);
+ return;
+ }
+ }
+ /* add a new one */
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_close (%s) new slot created\n", fname);
+ cc = xcalloc (1, sizeof *cc + strlen (fname));
+ strcpy (cc->fname, fname);
+ cc->fp = fp;
+ cc->next = close_cache;
+ close_cache = cc;
+}
+
+/*
+ * Do an direct_open on FNAME but first try to reuse one from the fd_cache
+ */
+static FILEP_OR_FD
+fd_cache_open (const char *fname, const char *mode)
+{
+ CLOSE_CACHE cc;
+
+ assert (fname);
+ for (cc = close_cache; cc; cc = cc->next)
+ {
+ if (cc->fp != INVALID_FP && !strcmp (cc->fname, fname))
+ {
+ FILEP_OR_FD fp = cc->fp;
+ cc->fp = INVALID_FP;
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_open (%s) using cached fp\n", fname);
+#ifdef HAVE_DOSISH_SYSTEM
+ if (SetFilePointer (fp, 0, NULL, FILE_BEGIN) == 0xffffffff)
+ {
+ log_error ("rewind file failed on handle %p: ec=%d\n",
+ fp, (int) GetLastError ());
+ fp = INVALID_FP;
+ }
+#else
+ if (lseek (fp, 0, SEEK_SET) == (off_t) - 1)
+ {
+ log_error ("can't rewind fd %d: %s\n", fp, strerror (errno));
+ fp = INVALID_FP;
+ }
+#endif
+ return fp;
+ }
+ }
+ if (DBG_IOBUF)
+ log_debug ("fd_cache_open (%s) not cached\n", fname);
+ return direct_open (fname, mode);
+}
+
+
+#endif /*FILE_FILTER_USES_STDIO */
+
+
+/****************
+ * Read data from a file into buf which has an allocated length of *LEN.
+ * return the number of read bytes in *LEN. OPAQUE is the FILE * of
+ * the stream. A is not used.
+ * control may be:
+ * IOBUFCTRL_INIT: called just before the function is linked into the
+ * list of function. This can be used to prepare internal
+ * data structures of the function.
+ * IOBUFCTRL_FREE: called just before the function is removed from the
+ * list of functions and can be used to release internal
+ * data structures or close a file etc.
+ * IOBUFCTRL_UNDERFLOW: called by iobuf_underflow to fill the buffer
+ * with new stuff. *RET_LEN is the available size of the
+ * buffer, and should be set to the number of bytes
+ * which were put into the buffer. The function
+ * returns 0 to indicate success, -1 on EOF and
+ * GPG_ERR_xxxxx for other errors.
+ *
+ * IOBUFCTRL_FLUSH: called by iobuf_flush() to write out the collected stuff.
+ * *RET_LAN is the number of bytes in BUF.
+ *
+ * IOBUFCTRL_CANCEL: send to all filters on behalf of iobuf_cancel. The
+ * filter may take appropriate action on this message.
+ */
+static int
+file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
+ size_t * ret_len)
+{
+ file_filter_ctx_t *a = opaque;
+ FILEP_OR_FD f = a->fp;
+ size_t size = *ret_len;
+ size_t nbytes = 0;
+ int rc = 0;
+
+#ifdef FILE_FILTER_USES_STDIO
+ if (control == IOBUFCTRL_UNDERFLOW)
+ {
+ assert (size); /* need a buffer */
+ if (feof (f))
+ { /* On terminals you could easiely read as many EOFs as you call */
+ rc = -1; /* fread() or fgetc() repeatly. Every call will block until you press */
+ *ret_len = 0; /* CTRL-D. So we catch this case before we call fread() again. */
+ }
+ else
+ {
+ clearerr (f);
+ nbytes = fread (buf, 1, size, f);
+ if (feof (f) && !nbytes)
+ {
+ rc = -1; /* okay: we can return EOF now. */
+ }
+ else if (ferror (f) && errno != EPIPE)
+ {
+ rc = gpg_error_from_errno (errno);
+ log_error ("%s: read error: %s\n", a->fname, strerror (errno));
+ }
+ *ret_len = nbytes;
+ }
+ }
+ else if (control == IOBUFCTRL_FLUSH)
+ {
+ if (size)
+ {
+ clearerr (f);
+ nbytes = fwrite (buf, 1, size, f);
+ if (ferror (f))
+ {
+ rc = gpg_error_from_errno (errno);
+ log_error ("%s: write error: %s\n", a->fname, strerror (errno));
+ }
+ }
+ *ret_len = nbytes;
+ }
+ else if (control == IOBUFCTRL_INIT)
+ {
+ a->keep_open = a->no_cache = 0;
+ }
+ else if (control == IOBUFCTRL_DESC)
+ {
+ *(char **) buf = "file_filter";
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+ if (f != stdin && f != stdout)
+ {
+ if (DBG_IOBUF)
+ log_debug ("%s: close fd %d\n", a->fname, fileno (f));
+ if (!a->keep_open)
+ fclose (f);
+ }
+ f = NULL;
+ xfree (a); /* we can free our context now */
+ }
+#else /* !stdio implementation */
+
+ if (control == IOBUFCTRL_UNDERFLOW)
+ {
+ assert (size); /* need a buffer */
+ if (a->eof_seen)
+ {
+ rc = -1;
+ *ret_len = 0;
+ }
+ else
+ {
+#ifdef HAVE_DOSISH_SYSTEM
+ unsigned long nread;
+
+ nbytes = 0;
+ if (!ReadFile (f, buf, size, &nread, NULL))
+ {
+ int ec = (int) GetLastError ();
+ if (ec != ERROR_BROKEN_PIPE)
+ {
+ rc = gpg_error_from_errno (ec);
+ log_error ("%s: read error: ec=%d\n", a->fname, ec);
+ }
+ }
+ else if (!nread)
+ {
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else
+ {
+ nbytes = nread;
+ }
+
+#else
+
+ int n;
+
+ nbytes = 0;
+ do
+ {
+ n = read (f, buf, size);
+ }
+ while (n == -1 && errno == EINTR);
+ if (n == -1)
+ { /* error */
+ if (errno != EPIPE)
+ {
+ rc = gpg_error_from_errno (errno);
+ log_error ("%s: read error: %s\n",
+ a->fname, strerror (errno));
+ }
+ }
+ else if (!n)
+ { /* eof */
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else
+ {
+ nbytes = n;
+ }
+#endif
+ *ret_len = nbytes;
+ }
+ }
+ else if (control == IOBUFCTRL_FLUSH)
+ {
+ if (size)
+ {
+#ifdef HAVE_DOSISH_SYSTEM
+ byte *p = buf;
+ unsigned long n;
+
+ nbytes = size;
+ do
+ {
+ if (size && !WriteFile (f, p, nbytes, &n, NULL))
+ {
+ int ec = (int) GetLastError ();
+ rc = gpg_error_from_errno (ec);
+ log_error ("%s: write error: ec=%d\n", a->fname, ec);
+ break;
+ }
+ p += n;
+ nbytes -= n;
+ }
+ while (nbytes);
+ nbytes = p - buf;
+#else
+ byte *p = buf;
+ int n;
+
+ nbytes = size;
+ do
+ {
+ do
+ {
+ n = write (f, p, nbytes);
+ }
+ while (n == -1 && errno == EINTR);
+ if (n > 0)
+ {
+ p += n;
+ nbytes -= n;
+ }
+ }
+ while (n != -1 && nbytes);
+ if (n == -1)
+ {
+ rc = gpg_error_from_errno (errno);
+ log_error ("%s: write error: %s\n", a->fname, strerror (errno));
+ }
+ nbytes = p - buf;
+#endif
+ }
+ *ret_len = nbytes;
+ }
+ else if (control == IOBUFCTRL_INIT)
+ {
+ a->eof_seen = 0;
+ a->keep_open = 0;
+ a->no_cache = 0;
+ }
+ else if (control == IOBUFCTRL_DESC)
+ {
+ *(char **) buf = "file_filter(fd)";
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+#ifdef HAVE_DOSISH_SYSTEM
+ if (f != FILEP_OR_FD_FOR_STDIN && f != FILEP_OR_FD_FOR_STDOUT)
+ {
+ if (DBG_IOBUF)
+ log_debug ("%s: close handle %p\n", a->fname, f);
+ if (!a->keep_open)
+ fd_cache_close (a->no_cache ? NULL : a->fname, f);
+ }
+#else
+ if ((int) f != 0 && (int) f != 1)
+ {
+ if (DBG_IOBUF)
+ log_debug ("%s: close fd %d\n", a->fname, f);
+ if (!a->keep_open)
+ fd_cache_close (a->no_cache ? NULL : a->fname, f);
+ }
+ f = INVALID_FP;
+#endif
+ xfree (a); /* we can free our context now */
+ }
+#endif /* !stdio implementation */
+ return rc;
+}
+
+#ifdef _WIN32
+/* Becuase sockets are an special object under Lose32 we have to
+ * use a special filter */
+static int
+sock_filter (void *opaque, int control, iobuf_t chain, byte * buf,
+ size_t * ret_len)
+{
+ sock_filter_ctx_t *a = opaque;
+ size_t size = *ret_len;
+ size_t nbytes = 0;
+ int rc = 0;
+
+ if (control == IOBUFCTRL_UNDERFLOW)
+ {
+ assert (size); /* need a buffer */
+ if (a->eof_seen)
+ {
+ rc = -1;
+ *ret_len = 0;
+ }
+ else
+ {
+ int nread;
+
+ nread = recv (a->sock, buf, size, 0);
+ if (nread == SOCKET_ERROR)
+ {
+ int ec = (int) WSAGetLastError ();
+ rc = gpg_error_from_errno (ec);
+ log_error ("socket read error: ec=%d\n", ec);
+ }
+ else if (!nread)
+ {
+ a->eof_seen = 1;
+ rc = -1;
+ }
+ else
+ {
+ nbytes = nread;
+ }
+ *ret_len = nbytes;
+ }
+ }
+ else if (control == IOBUFCTRL_FLUSH)
+ {
+ if (size)
+ {
+ byte *p = buf;
+ int n;
+
+ nbytes = size;
+ do
+ {
+ n = send (a->sock, p, nbytes, 0);
+ if (n == SOCKET_ERROR)
+ {
+ int ec = (int) WSAGetLastError ();
+ rc = gpg_error_from_errno (ec);
+ log_error ("socket write error: ec=%d\n", ec);
+ break;
+ }
+ p += n;
+ nbytes -= n;
+ }
+ while (nbytes);
+ nbytes = p - buf;
+ }
+ *ret_len = nbytes;
+ }
+ else if (control == IOBUFCTRL_INIT)
+ {
+ a->eof_seen = 0;
+ a->keep_open = 0;
+ a->no_cache = 0;
+ }
+ else if (control == IOBUFCTRL_DESC)
+ {
+ *(char **) buf = "sock_filter";
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+ if (!a->keep_open)
+ closesocket (a->sock);
+ xfree (a); /* we can free our context now */
+ }
+ return rc;
+}
+#endif /*_WIN32*/
+
+/****************
+ * This is used to implement the block write mode.
+ * Block reading is done on a byte by byte basis in readbyte(),
+ * without a filter
+ */
+static int
+block_filter (void *opaque, int control, iobuf_t chain, byte * buffer,
+ size_t * ret_len)
+{
+ block_filter_ctx_t *a = opaque;
+ char *buf = (char *)buffer;
+ size_t size = *ret_len;
+ int c, needed, rc = 0;
+ char *p;
+
+ if (control == IOBUFCTRL_UNDERFLOW)
+ {
+ size_t n = 0;
+
+ p = buf;
+ assert (size); /* need a buffer */
+ if (a->eof) /* don't read any further */
+ rc = -1;
+ while (!rc && size)
+ {
+ if (!a->size)
+ { /* get the length bytes */
+ if (a->partial == 2)
+ {
+ a->eof = 1;
+ if (!n)
+ rc = -1;
+ break;
+ }
+ else if (a->partial)
+ {
+ /* These OpenPGP introduced huffman like encoded length
+ * bytes are really a mess :-( */
+ if (a->first_c)
+ {
+ c = a->first_c;
+ a->first_c = 0;
+ }
+ else if ((c = iobuf_get (chain)) == -1)
+ {
+ log_error ("block_filter: 1st length byte missing\n");
+ rc = GPG_ERR_BAD_DATA;
+ break;
+ }
+ if (c < 192)
+ {
+ a->size = c;
+ a->partial = 2;
+ if (!a->size)
+ {
+ a->eof = 1;
+ if (!n)
+ rc = -1;
+ break;
+ }
+ }
+ else if (c < 224)
+ {
+ a->size = (c - 192) * 256;
+ if ((c = iobuf_get (chain)) == -1)
+ {
+ log_error
+ ("block_filter: 2nd length byte missing\n");
+ rc = GPG_ERR_BAD_DATA;
+ break;
+ }
+ a->size += c + 192;
+ a->partial = 2;
+ if (!a->size)
+ {
+ a->eof = 1;
+ if (!n)
+ rc = -1;
+ break;
+ }
+ }
+ else if (c == 255)
+ {
+ a->size = iobuf_get (chain) << 24;
+ a->size |= iobuf_get (chain) << 16;
+ a->size |= iobuf_get (chain) << 8;
+ if ((c = iobuf_get (chain)) == -1)
+ {
+ log_error ("block_filter: invalid 4 byte length\n");
+ rc = GPG_ERR_BAD_DATA;
+ break;
+ }
+ a->size |= c;
+ a->partial = 2;
+ if (!a->size)
+ {
+ a->eof = 1;
+ if (!n)
+ rc = -1;
+ break;
+ }
+ }
+ else
+ { /* Next partial body length. */
+ a->size = 1 << (c & 0x1f);
+ }
+ /* log_debug("partial: ctx=%p c=%02x size=%u\n", a, c, a->size); */
+ }
+ else
+ BUG ();
+ }
+
+ while (!rc && size && a->size)
+ {
+ needed = size < a->size ? size : a->size;
+ c = iobuf_read (chain, p, needed);
+ if (c < needed)
+ {
+ if (c == -1)
+ c = 0;
+ log_error
+ ("block_filter %p: read error (size=%lu,a->size=%lu)\n",
+ a, (ulong) size + c, (ulong) a->size + c);
+ rc = GPG_ERR_BAD_DATA;
+ }
+ else
+ {
+ size -= c;
+ a->size -= c;
+ p += c;
+ n += c;
+ }
+ }
+ }
+ *ret_len = n;
+ }
+ else if (control == IOBUFCTRL_FLUSH)
+ {
+ if (a->partial)
+ { /* the complicated openpgp scheme */
+ size_t blen, n, nbytes = size + a->buflen;
+
+ assert (a->buflen <= OP_MIN_PARTIAL_CHUNK);
+ if (nbytes < OP_MIN_PARTIAL_CHUNK)
+ {
+ /* not enough to write a partial block out; so we store it */
+ if (!a->buffer)
+ a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK);
+ memcpy (a->buffer + a->buflen, buf, size);
+ a->buflen += size;
+ }
+ else
+ { /* okay, we can write out something */
+ /* do this in a loop to use the most efficient block lengths */
+ p = buf;
+ do
+ {
+ /* find the best matching block length - this is limited
+ * by the size of the internal buffering */
+ for (blen = OP_MIN_PARTIAL_CHUNK * 2,
+ c = OP_MIN_PARTIAL_CHUNK_2POW + 1; blen <= nbytes;
+ blen *= 2, c++)
+ ;
+ blen /= 2;
+ c--;
+ /* write the partial length header */
+ assert (c <= 0x1f); /*;-) */
+ c |= 0xe0;
+ iobuf_put (chain, c);
+ if ((n = a->buflen))
+ { /* write stuff from the buffer */
+ assert (n == OP_MIN_PARTIAL_CHUNK);
+ if (iobuf_write (chain, a->buffer, n))
+ rc = gpg_error_from_errno (errno);
+ a->buflen = 0;
+ nbytes -= n;
+ }
+ if ((n = nbytes) > blen)
+ n = blen;
+ if (n && iobuf_write (chain, p, n))
+ rc = gpg_error_from_errno (errno);
+ p += n;
+ nbytes -= n;
+ }
+ while (!rc && nbytes >= OP_MIN_PARTIAL_CHUNK);
+ /* store the rest in the buffer */
+ if (!rc && nbytes)
+ {
+ assert (!a->buflen);
+ assert (nbytes < OP_MIN_PARTIAL_CHUNK);
+ if (!a->buffer)
+ a->buffer = xmalloc (OP_MIN_PARTIAL_CHUNK);
+ memcpy (a->buffer, p, nbytes);
+ a->buflen = nbytes;
+ }
+ }
+ }
+ else
+ BUG ();
+ }
+ else if (control == IOBUFCTRL_INIT)
+ {
+ if (DBG_IOBUF)
+ log_debug ("init block_filter %p\n", a);
+ if (a->partial)
+ a->count = 0;
+ else if (a->use == 1)
+ a->count = a->size = 0;
+ else
+ a->count = a->size; /* force first length bytes */
+ a->eof = 0;
+ a->buffer = NULL;
+ a->buflen = 0;
+ }
+ else if (control == IOBUFCTRL_DESC)
+ {
+ *(char **) buf = "block_filter";
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+ if (a->use == 2)
+ { /* write the end markers */
+ if (a->partial)
+ {
+ u32 len;
+ /* write out the remaining bytes without a partial header
+ * the length of this header may be 0 - but if it is
+ * the first block we are not allowed to use a partial header
+ * and frankly we can't do so, because this length must be
+ * a power of 2. This is _really_ complicated because we
+ * have to check the possible length of a packet prior
+ * to it's creation: a chain of filters becomes complicated
+ * and we need a lot of code to handle compressed packets etc.
+ * :-(((((((
+ */
+ /* construct header */
+ len = a->buflen;
+ /*log_debug("partial: remaining length=%u\n", len ); */
+ if (len < 192)
+ rc = iobuf_put (chain, len);
+ else if (len < 8384)
+ {
+ if (!(rc = iobuf_put (chain, ((len - 192) / 256) + 192)))
+ rc = iobuf_put (chain, ((len - 192) % 256));
+ }
+ else
+ { /* use a 4 byte header */
+ if (!(rc = iobuf_put (chain, 0xff)))
+ if (!(rc = iobuf_put (chain, (len >> 24) & 0xff)))
+ if (!(rc = iobuf_put (chain, (len >> 16) & 0xff)))
+ if (!(rc = iobuf_put (chain, (len >> 8) & 0xff)))
+ rc = iobuf_put (chain, len & 0xff);
+ }
+ if (!rc && len)
+ rc = iobuf_write (chain, a->buffer, len);
+ if (rc)
+ {
+ log_error ("block_filter: write error: %s\n",
+ strerror (errno));
+ rc = gpg_error_from_errno (errno);
+ }
+ xfree (a->buffer);
+ a->buffer = NULL;
+ a->buflen = 0;
+ }
+ else
+ BUG ();
+ }
+ else if (a->size)
+ {
+ log_error ("block_filter: pending bytes!\n");
+ }
+ if (DBG_IOBUF)
+ log_debug ("free block_filter %p\n", a);
+ xfree (a); /* we can free our context now */
+ }
+
+ return rc;
+}
+
+
+static void
+print_chain (iobuf_t a)
+{
+ if (!DBG_IOBUF)
+ return;
+ for (; a; a = a->chain)
+ {
+ size_t dummy_len = 0;
+ const char *desc = "[none]";
+
+ if (a->filter)
+ a->filter (a->filter_ov, IOBUFCTRL_DESC, NULL,
+ (byte *) & desc, &dummy_len);
+
+ log_debug ("iobuf chain: %d.%d `%s' filter_eof=%d start=%d len=%d\n",
+ a->no, a->subno, desc, a->filter_eof,
+ (int) a->d.start, (int) a->d.len);
+ }
+}
+
+int
+iobuf_print_chain (iobuf_t a)
+{
+ print_chain (a);
+ return 0;
+}
+
+/****************
+ * Allocate a new io buffer, with no function assigned.
+ * Use is the desired usage: 1 for input, 2 for output, 3 for temp buffer
+ * BUFSIZE is a suggested buffer size.
+ */
+iobuf_t
+iobuf_alloc (int use, size_t bufsize)
+{
+ iobuf_t a;
+ static int number = 0;
+
+ a = xcalloc (1, sizeof *a);
+ a->use = use;
+ a->d.buf = xmalloc (bufsize);
+ a->d.size = bufsize;
+ a->no = ++number;
+ a->subno = 0;
+ a->opaque = NULL;
+ a->real_fname = NULL;
+ return a;
+}
+
+int
+iobuf_close (iobuf_t a)
+{
+ iobuf_t a2;
+ size_t dummy_len = 0;
+ int rc = 0;
+
+ if (a && a->directfp)
+ {
+ fclose (a->directfp);
+ xfree (a->real_fname);
+ if (DBG_IOBUF)
+ log_debug ("iobuf_close -> %p\n", a->directfp);
+ return 0;
+ }
+
+ for (; a && !rc; a = a2)
+ {
+ a2 = a->chain;
+ if (a->use == 2 && (rc = iobuf_flush (a)))
+ log_error ("iobuf_flush failed on close: %s\n", gpg_strerror (rc));
+
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: close `%s'\n", a->no, a->subno, a->desc);
+ if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_FREE,
+ a->chain, NULL, &dummy_len)))
+ log_error ("IOBUFCTRL_FREE failed on close: %s\n", gpg_strerror (rc));
+ xfree (a->real_fname);
+ if (a->d.buf)
+ {
+ memset (a->d.buf, 0, a->d.size); /* erase the buffer */
+ xfree (a->d.buf);
+ }
+ xfree (a);
+ }
+ return rc;
+}
+
+int
+iobuf_cancel (iobuf_t a)
+{
+ const char *s;
+ iobuf_t a2;
+ int rc;
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ char *remove_name = NULL;
+#endif
+
+ if (a && a->use == 2)
+ {
+ s = iobuf_get_real_fname (a);
+ if (s && *s)
+ {
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ remove_name = xstrdup (s);
+#else
+ remove (s);
+#endif
+ }
+ }
+
+ /* send a cancel message to all filters */
+ for (a2 = a; a2; a2 = a2->chain)
+ {
+ size_t dummy;
+ if (a2->filter)
+ a2->filter (a2->filter_ov, IOBUFCTRL_CANCEL, a2->chain, NULL, &dummy);
+ }
+
+ rc = iobuf_close (a);
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ if (remove_name)
+ {
+ /* Argg, MSDOS does not allow to remove open files. So
+ * we have to do it here */
+ remove (remove_name);
+ xfree (remove_name);
+ }
+#endif
+ return rc;
+}
+
+
+/****************
+ * create a temporary iobuf, which can be used to collect stuff
+ * in an iobuf and later be written by iobuf_write_temp() to another
+ * iobuf.
+ */
+iobuf_t
+iobuf_temp ()
+{
+ iobuf_t a;
+
+ a = iobuf_alloc (3, 8192);
+
+ return a;
+}
+
+iobuf_t
+iobuf_temp_with_content (const char *buffer, size_t length)
+{
+ iobuf_t a;
+
+ a = iobuf_alloc (3, length);
+ memcpy (a->d.buf, buffer, length);
+ a->d.len = length;
+
+ return a;
+}
+
+void
+iobuf_enable_special_filenames (int yes)
+{
+ special_names_enabled = yes;
+}
+
+
+/* See whether the filename has the form "-&nnnn", where n is a
+ non-zero number. Returns this number or -1 if it is not the
+ case. */
+static int
+check_special_filename (const char *fname)
+{
+ if (special_names_enabled && fname && *fname == '-' && fname[1] == '&')
+ {
+ int i;
+
+ fname += 2;
+ for (i = 0; digitp (fname+i); i++)
+ ;
+ if (!fname[i])
+ return atoi (fname);
+ }
+ return -1;
+}
+
+
+/* This fucntion returns true if FNAME indicates a PIPE (stdout or
+ stderr) or a special file name if those are enabled. */
+int
+iobuf_is_pipe_filename (const char *fname)
+{
+ if (!fname || (*fname=='-' && !fname[1]) )
+ return 1;
+ return check_special_filename (fname) != -1;
+}
+
+/****************
+ * Create a head iobuf for reading from a file
+ * returns: NULL if an error occures and sets errno
+ */
+iobuf_t
+iobuf_open (const char *fname)
+{
+ iobuf_t a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+ int print_only = 0;
+ int fd;
+
+ if (!fname || (*fname == '-' && !fname[1]))
+ {
+ fp = FILEP_OR_FD_FOR_STDIN;
+#ifdef USE_SETMODE
+ setmode (my_fileno (fp), O_BINARY);
+#endif
+ fname = "[stdin]";
+ print_only = 1;
+ }
+ else if ((fd = check_special_filename (fname)) != -1)
+ return iobuf_fdopen (translate_file_handle (fd, 0), "rb");
+ else if ((fp = my_fopen_ro (fname, "rb")) == INVALID_FP)
+ return NULL;
+ a = iobuf_alloc (1, 8192);
+ fcx = xmalloc (sizeof *fcx + strlen (fname));
+ fcx->fp = fp;
+ fcx->print_only_name = print_only;
+ strcpy (fcx->fname, fname);
+ if (!print_only)
+ a->real_fname = xstrdup (fname);
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: open `%s' fd=%d\n",
+ a->no, a->subno, fname, (int) my_fileno (fcx->fp));
+
+ return a;
+}
+
+/****************
+ * Create a head iobuf for reading from a file
+ * returns: NULL if an error occures and sets errno
+ */
+iobuf_t
+iobuf_fdopen (int fd, const char *mode)
+{
+ iobuf_t a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+#ifdef FILE_FILTER_USES_STDIO
+ if (!(fp = fdopen (fd, mode)))
+ return NULL;
+#else
+ fp = (FILEP_OR_FD) fd;
+#endif
+ a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, 8192);
+ fcx = xmalloc (sizeof *fcx + 20);
+ fcx->fp = fp;
+ fcx->print_only_name = 1;
+ sprintf (fcx->fname, "[fd %d]", fd);
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: fdopen `%s'\n", a->no, a->subno, fcx->fname);
+ iobuf_ioctl (a, 3, 1, NULL); /* disable fd caching */
+ return a;
+}
+
+
+iobuf_t
+iobuf_sockopen (int fd, const char *mode)
+{
+ iobuf_t a;
+#ifdef _WIN32
+ sock_filter_ctx_t *scx;
+ size_t len;
+
+ a = iobuf_alloc (strchr (mode, 'w') ? 2 : 1, 8192);
+ scx = xmalloc (sizeof *scx + 25);
+ scx->sock = fd;
+ scx->print_only_name = 1;
+ sprintf (scx->fname, "[sock %d]", fd);
+ a->filter = sock_filter;
+ a->filter_ov = scx;
+ sock_filter (scx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ sock_filter (scx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: sockopen `%s'\n", a->no, a->subno, scx->fname);
+ iobuf_ioctl (a, 3, 1, NULL); /* disable fd caching */
+#else
+ a = iobuf_fdopen (fd, mode);
+#endif
+ return a;
+}
+
+/****************
+ * create an iobuf for writing to a file; the file will be created.
+ */
+iobuf_t
+iobuf_create (const char *fname)
+{
+ iobuf_t a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+ int print_only = 0;
+ int fd;
+
+ if (!fname || (*fname == '-' && !fname[1]))
+ {
+ fp = FILEP_OR_FD_FOR_STDOUT;
+#ifdef USE_SETMODE
+ setmode (my_fileno (fp), O_BINARY);
+#endif
+ fname = "[stdout]";
+ print_only = 1;
+ }
+ else if ((fd = check_special_filename (fname)) != -1)
+ return iobuf_fdopen (translate_file_handle (fd, 1), "wb");
+ else if ((fp = my_fopen (fname, "wb")) == INVALID_FP)
+ return NULL;
+ a = iobuf_alloc (2, 8192);
+ fcx = xmalloc (sizeof *fcx + strlen (fname));
+ fcx->fp = fp;
+ fcx->print_only_name = print_only;
+ strcpy (fcx->fname, fname);
+ if (!print_only)
+ a->real_fname = xstrdup (fname);
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: create `%s'\n", a->no, a->subno, a->desc);
+
+ return a;
+}
+
+/****************
+ * append to an iobuf; if the file does not exist, create it.
+ * cannot be used for stdout.
+ * Note: This is not used.
+ */
+#if 0 /* not used */
+iobuf_t
+iobuf_append (const char *fname)
+{
+ iobuf_t a;
+ FILE *fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+ if (!fname)
+ return NULL;
+ else if (!(fp = my_fopen (fname, "ab")))
+ return NULL;
+ a = iobuf_alloc (2, 8192);
+ fcx = m_alloc (sizeof *fcx + strlen (fname));
+ fcx->fp = fp;
+ strcpy (fcx->fname, fname);
+ a->real_fname = m_strdup (fname);
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: append `%s'\n", a->no, a->subno, a->desc);
+
+ return a;
+}
+#endif
+
+iobuf_t
+iobuf_openrw (const char *fname)
+{
+ iobuf_t a;
+ FILEP_OR_FD fp;
+ file_filter_ctx_t *fcx;
+ size_t len;
+
+ if (!fname)
+ return NULL;
+ else if ((fp = my_fopen (fname, "r+b")) == INVALID_FP)
+ return NULL;
+ a = iobuf_alloc (2, 8192);
+ fcx = xmalloc (sizeof *fcx + strlen (fname));
+ fcx->fp = fp;
+ strcpy (fcx->fname, fname);
+ a->real_fname = xstrdup (fname);
+ a->filter = file_filter;
+ a->filter_ov = fcx;
+ file_filter (fcx, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &len);
+ file_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: openrw `%s'\n", a->no, a->subno, a->desc);
+
+ return a;
+}
+
+
+int
+iobuf_ioctl (iobuf_t a, int cmd, int intval, void *ptrval)
+{
+ if (cmd == 1)
+ { /* keep system filepointer/descriptor open */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: ioctl `%s' keep=%d\n",
+ a ? a->no : -1, a ? a->subno : -1, a ? a->desc : "?",
+ intval);
+ for (; a; a = a->chain)
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ b->keep_open = intval;
+ return 0;
+ }
+#ifdef _WIN32
+ else if (!a->chain && a->filter == sock_filter)
+ {
+ sock_filter_ctx_t *b = a->filter_ov;
+ b->keep_open = intval;
+ return 0;
+ }
+#endif
+ }
+ else if (cmd == 2)
+ { /* invalidate cache */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-*.*: ioctl `%s' invalidate\n",
+ ptrval ? (char *) ptrval : "?");
+ if (!a && !intval && ptrval)
+ {
+#ifndef FILE_FILTER_USES_STDIO
+ fd_cache_invalidate (ptrval);
+#endif
+ return 0;
+ }
+ }
+ else if (cmd == 3)
+ { /* disallow/allow caching */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: ioctl `%s' no_cache=%d\n",
+ a ? a->no : -1, a ? a->subno : -1, a ? a->desc : "?",
+ intval);
+ for (; a; a = a->chain)
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ b->no_cache = intval;
+ return 0;
+ }
+#ifdef _WIN32
+ else if (!a->chain && a->filter == sock_filter)
+ {
+ sock_filter_ctx_t *b = a->filter_ov;
+ b->no_cache = intval;
+ return 0;
+ }
+#endif
+ }
+
+ return -1;
+}
+
+
+/****************
+ * Register an i/o filter.
+ */
+int
+iobuf_push_filter (iobuf_t a,
+ int (*f) (void *opaque, int control,
+ iobuf_t chain, byte * buf, size_t * len),
+ void *ov)
+{
+ return iobuf_push_filter2 (a, f, ov, 0);
+}
+
+int
+iobuf_push_filter2 (iobuf_t a,
+ int (*f) (void *opaque, int control,
+ iobuf_t chain, byte * buf, size_t * len),
+ void *ov, int rel_ov)
+{
+ iobuf_t b;
+ size_t dummy_len = 0;
+ int rc = 0;
+
+ if (a->directfp)
+ BUG ();
+
+ if (a->use == 2 && (rc = iobuf_flush (a)))
+ return rc;
+ /* make a copy of the current stream, so that
+ * A is the new stream and B the original one.
+ * The contents of the buffers are transferred to the
+ * new stream.
+ */
+ b = xmalloc (sizeof *b);
+ memcpy (b, a, sizeof *b);
+ /* fixme: it is stupid to keep a copy of the name at every level
+ * but we need the name somewhere because the name known by file_filter
+ * may have been released when we need the name of the file */
+ b->real_fname = a->real_fname ? xstrdup (a->real_fname) : NULL;
+ /* remove the filter stuff from the new stream */
+ a->filter = NULL;
+ a->filter_ov = NULL;
+ a->filter_ov_owner = 0;
+ a->filter_eof = 0;
+ if (a->use == 3)
+ a->use = 2; /* make a write stream from a temp stream */
+
+ if (a->use == 2)
+ { /* allocate a fresh buffer for the
+ original stream */
+ b->d.buf = xmalloc (a->d.size);
+ b->d.len = 0;
+ b->d.start = 0;
+ }
+ else
+ { /* allocate a fresh buffer for the new
+ stream */
+ a->d.buf = xmalloc (a->d.size);
+ a->d.len = 0;
+ a->d.start = 0;
+ }
+ /* disable nlimit for the new stream */
+ a->ntotal = b->ntotal + b->nbytes;
+ a->nlimit = a->nbytes = 0;
+ a->nofast &= ~1;
+ /* make a link from the new stream to the original stream */
+ a->chain = b;
+ a->opaque = b->opaque;
+
+ /* setup the function on the new stream */
+ a->filter = f;
+ a->filter_ov = ov;
+ a->filter_ov_owner = rel_ov;
+
+ a->subno = b->subno + 1;
+ f (ov, IOBUFCTRL_DESC, NULL, (byte *) & a->desc, &dummy_len);
+
+ if (DBG_IOBUF)
+ {
+ log_debug ("iobuf-%d.%d: push `%s'\n", a->no, a->subno, a->desc);
+ print_chain (a);
+ }
+
+ /* now we can initialize the new function if we have one */
+ if (a->filter && (rc = a->filter (a->filter_ov, IOBUFCTRL_INIT, a->chain,
+ NULL, &dummy_len)))
+ log_error ("IOBUFCTRL_INIT failed: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+/****************
+ * Remove an i/o filter.
+ */
+static int
+pop_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ iobuf_t chain, byte * buf, size_t * len),
+ void *ov)
+{
+ iobuf_t b;
+ size_t dummy_len = 0;
+ int rc = 0;
+
+ if (a->directfp)
+ BUG ();
+
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: pop `%s'\n", a->no, a->subno, a->desc);
+ if (!a->filter)
+ { /* this is simple */
+ b = a->chain;
+ assert (b);
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+ memcpy (a, b, sizeof *a);
+ xfree (b);
+ return 0;
+ }
+ for (b = a; b; b = b->chain)
+ if (b->filter == f && (!ov || b->filter_ov == ov))
+ break;
+ if (!b)
+ log_bug ("pop_filter(): filter function not found\n");
+
+ /* flush this stream if it is an output stream */
+ if (a->use == 2 && (rc = iobuf_flush (b)))
+ {
+ log_error ("iobuf_flush failed in pop_filter: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+ /* and tell the filter to free it self */
+ if (b->filter && (rc = b->filter (b->filter_ov, IOBUFCTRL_FREE, b->chain,
+ NULL, &dummy_len)))
+ {
+ log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+ if (b->filter_ov && b->filter_ov_owner)
+ {
+ xfree (b->filter_ov);
+ b->filter_ov = NULL;
+ }
+
+
+ /* and see how to remove it */
+ if (a == b && !b->chain)
+ log_bug ("can't remove the last filter from the chain\n");
+ else if (a == b)
+ { /* remove the first iobuf from the chain */
+ /* everything from b is copied to a. This is save because
+ * a flush has been done on the to be removed entry
+ */
+ b = a->chain;
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+ memcpy (a, b, sizeof *a);
+ xfree (b);
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: popped filter\n", a->no, a->subno);
+ }
+ else if (!b->chain)
+ { /* remove the last iobuf from the chain */
+ log_bug ("Ohh jeee, trying to remove a head filter\n");
+ }
+ else
+ { /* remove an intermediate iobuf from the chain */
+ log_bug ("Ohh jeee, trying to remove an intermediate filter\n");
+ }
+
+ return rc;
+}
+
+
+/****************
+ * read underflow: read more bytes into the buffer and return
+ * the first byte or -1 on EOF.
+ */
+static int
+underflow (iobuf_t a)
+{
+ size_t len;
+ int rc;
+
+ assert (a->d.start == a->d.len);
+ if (a->use == 3)
+ return -1; /* EOF because a temp buffer can't do an underflow */
+
+ if (a->filter_eof)
+ {
+ if (a->chain)
+ {
+ iobuf_t b = a->chain;
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: pop `%s' in underflow\n",
+ a->no, a->subno, a->desc);
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+ memcpy (a, b, sizeof *a);
+ xfree (b);
+ print_chain (a);
+ }
+ else
+ a->filter_eof = 0; /* for the top level filter */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: eof (due to filter eof)\n",
+ a->no, a->subno);
+ return -1; /* return one(!) EOF */
+ }
+ if (a->error)
+ {
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: error\n", a->no, a->subno);
+ return -1;
+ }
+
+ if (a->directfp)
+ {
+ FILE *fp = a->directfp;
+
+ len = fread (a->d.buf, 1, a->d.size, fp);
+ if (len < a->d.size)
+ {
+ if (ferror (fp))
+ a->error = gpg_error_from_errno (errno);
+ }
+ a->d.len = len;
+ a->d.start = 0;
+ return len ? a->d.buf[a->d.start++] : -1;
+ }
+
+
+ if (a->filter)
+ {
+ len = a->d.size;
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: req=%lu\n",
+ a->no, a->subno, (ulong) len);
+ rc = a->filter (a->filter_ov, IOBUFCTRL_UNDERFLOW, a->chain,
+ a->d.buf, &len);
+ if (DBG_IOBUF)
+ {
+ log_debug ("iobuf-%d.%d: underflow: got=%lu rc=%d\n",
+ a->no, a->subno, (ulong) len, rc);
+/* if( a->no == 1 ) */
+/* log_hexdump (" data:", a->d.buf, len); */
+ }
+ if (a->use == 1 && rc == -1)
+ { /* EOF: we can remove the filter */
+ size_t dummy_len = 0;
+
+ /* and tell the filter to free itself */
+ if ((rc = a->filter (a->filter_ov, IOBUFCTRL_FREE, a->chain,
+ NULL, &dummy_len)))
+ log_error ("IOBUFCTRL_FREE failed: %s\n", gpg_strerror (rc));
+ if (a->filter_ov && a->filter_ov_owner)
+ {
+ xfree (a->filter_ov);
+ a->filter_ov = NULL;
+ }
+ a->filter = NULL;
+ a->desc = NULL;
+ a->filter_ov = NULL;
+ a->filter_eof = 1;
+ if (!len && a->chain)
+ {
+ iobuf_t b = a->chain;
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: pop `%s' in underflow (!len)\n",
+ a->no, a->subno, a->desc);
+ xfree (a->d.buf);
+ xfree (a->real_fname);
+ memcpy (a, b, sizeof *a);
+ xfree (b);
+ print_chain (a);
+ }
+ }
+ else if (rc)
+ a->error = rc;
+
+ if (!len)
+ {
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: eof\n", a->no, a->subno);
+ return -1;
+ }
+ a->d.len = len;
+ a->d.start = 0;
+ return a->d.buf[a->d.start++];
+ }
+ else
+ {
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: underflow: eof (no filter)\n",
+ a->no, a->subno);
+ return -1; /* no filter; return EOF */
+ }
+}
+
+
+int
+iobuf_flush (iobuf_t a)
+{
+ size_t len;
+ int rc;
+
+ if (a->directfp)
+ return 0;
+
+ if (a->use == 3)
+ { /* increase the temp buffer */
+ unsigned char *newbuf;
+ size_t newsize = a->d.size + 8192;
+
+ if (DBG_IOBUF)
+ log_debug ("increasing temp iobuf from %lu to %lu\n",
+ (ulong) a->d.size, (ulong) newsize);
+ newbuf = xmalloc (newsize);
+ memcpy (newbuf, a->d.buf, a->d.len);
+ xfree (a->d.buf);
+ a->d.buf = newbuf;
+ a->d.size = newsize;
+ return 0;
+ }
+ else if (a->use != 2)
+ log_bug ("flush on non-output iobuf\n");
+ else if (!a->filter)
+ log_bug ("iobuf_flush: no filter\n");
+ len = a->d.len;
+ rc = a->filter (a->filter_ov, IOBUFCTRL_FLUSH, a->chain, a->d.buf, &len);
+ if (!rc && len != a->d.len)
+ {
+ log_info ("iobuf_flush did not write all!\n");
+ rc = GPG_ERR_INTERNAL;
+ }
+ else if (rc)
+ a->error = rc;
+ a->d.len = 0;
+
+ return rc;
+}
+
+
+/****************
+ * Read a byte from the iobuf; returns -1 on EOF
+ */
+int
+iobuf_readbyte (iobuf_t a)
+{
+ int c;
+
+ /* nlimit does not work together with unget */
+ /* nbytes is also not valid! */
+ if (a->unget.buf)
+ {
+ if (a->unget.start < a->unget.len)
+ return a->unget.buf[a->unget.start++];
+ xfree (a->unget.buf);
+ a->unget.buf = NULL;
+ a->nofast &= ~2;
+ }
+
+ if (a->nlimit && a->nbytes >= a->nlimit)
+ return -1; /* forced EOF */
+
+ if (a->d.start < a->d.len)
+ {
+ c = a->d.buf[a->d.start++];
+ }
+ else if ((c = underflow (a)) == -1)
+ return -1; /* EOF */
+
+ a->nbytes++;
+ return c;
+}
+
+
+int
+iobuf_read (iobuf_t a, void *buffer, unsigned int buflen)
+{
+ unsigned char *buf = (unsigned char *)buffer;
+ int c, n;
+
+ if (a->unget.buf || a->nlimit)
+ {
+ /* handle special cases */
+ for (n = 0; n < buflen; n++)
+ {
+ if ((c = iobuf_readbyte (a)) == -1)
+ {
+ if (!n)
+ return -1; /* eof */
+ break;
+ }
+ else if (buf)
+ *buf = c;
+ if (buf)
+ buf++;
+ }
+ return n;
+ }
+
+ n = 0;
+ do
+ {
+ if (n < buflen && a->d.start < a->d.len)
+ {
+ unsigned size = a->d.len - a->d.start;
+ if (size > buflen - n)
+ size = buflen - n;
+ if (buf)
+ memcpy (buf, a->d.buf + a->d.start, size);
+ n += size;
+ a->d.start += size;
+ if (buf)
+ buf += size;
+ }
+ if (n < buflen)
+ {
+ if ((c = underflow (a)) == -1)
+ {
+ a->nbytes += n;
+ return n ? n : -1 /*EOF*/;
+ }
+ if (buf)
+ *buf++ = c;
+ n++;
+ }
+ }
+ while (n < buflen);
+ a->nbytes += n;
+ return n;
+}
+
+
+/****************
+ * Have a look at the iobuf.
+ * NOTE: This only works in special cases.
+ */
+int
+iobuf_peek (iobuf_t a, byte * buf, unsigned buflen)
+{
+ int n = 0;
+
+ if (a->filter_eof)
+ return -1;
+
+ if (!(a->d.start < a->d.len))
+ {
+ if (underflow (a) == -1)
+ return -1;
+ /* and unget this character */
+ assert (a->d.start == 1);
+ a->d.start = 0;
+ }
+
+ for (n = 0; n < buflen && (a->d.start + n) < a->d.len; n++, buf++)
+ *buf = a->d.buf[n];
+ return n;
+}
+
+
+
+
+int
+iobuf_writebyte (iobuf_t a, unsigned int c)
+{
+ int rc;
+
+ if (a->directfp)
+ BUG ();
+
+ if (a->d.len == a->d.size)
+ if ((rc=iobuf_flush (a)))
+ return rc;
+
+ assert (a->d.len < a->d.size);
+ a->d.buf[a->d.len++] = c;
+ return 0;
+}
+
+
+int
+iobuf_write (iobuf_t a, const void *buffer, unsigned int buflen)
+{
+ const unsigned char *buf = (const unsigned char *)buffer;
+ int rc;
+
+ if (a->directfp)
+ BUG ();
+
+ do
+ {
+ if (buflen && a->d.len < a->d.size)
+ {
+ unsigned size = a->d.size - a->d.len;
+ if (size > buflen)
+ size = buflen;
+ memcpy (a->d.buf + a->d.len, buf, size);
+ buflen -= size;
+ buf += size;
+ a->d.len += size;
+ }
+ if (buflen)
+ {
+ rc = iobuf_flush (a);
+ if (rc)
+ return rc;
+ }
+ }
+ while (buflen);
+ return 0;
+}
+
+
+int
+iobuf_writestr (iobuf_t a, const char *buf)
+{
+ int rc;
+
+ for (; *buf; buf++)
+ if ((rc=iobuf_writebyte (a, *buf)))
+ return rc;
+ return 0;
+}
+
+
+
+/****************
+ * copy the contents of TEMP to A.
+ */
+int
+iobuf_write_temp (iobuf_t a, iobuf_t temp)
+{
+ while (temp->chain)
+ pop_filter (temp, temp->filter, NULL);
+ return iobuf_write (a, temp->d.buf, temp->d.len);
+}
+
+/****************
+ * copy the contents of the temp io stream to BUFFER.
+ */
+size_t
+iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen)
+{
+ size_t n = a->d.len;
+
+ if (n > buflen)
+ n = buflen;
+ memcpy (buffer, a->d.buf, n);
+ return n;
+}
+
+
+/****************
+ * Call this function to terminate processing of the temp stream
+ * without closing it. This removes all filters from the stream
+ * makes sure that iobuf_get_temp_{buffer,length}() returns correct
+ * values.
+ */
+void
+iobuf_flush_temp (iobuf_t temp)
+{
+ while (temp->chain)
+ pop_filter (temp, temp->filter, NULL);
+}
+
+
+/****************
+ * Set a limit on how many bytes may be read from the input stream A.
+ * Setting the limit to 0 disables this feature.
+ */
+void
+iobuf_set_limit (iobuf_t a, off_t nlimit)
+{
+ if (nlimit)
+ a->nofast |= 1;
+ else
+ a->nofast &= ~1;
+ a->nlimit = nlimit;
+ a->ntotal += a->nbytes;
+ a->nbytes = 0;
+}
+
+
+
+/* Return the length of an open file A. IF OVERFLOW is not NULL it
+ will be set to true if the file is larger than what off_t can cope
+ with. The function return 0 on error or on overflow condition. */
+off_t
+iobuf_get_filelength (iobuf_t a, int *overflow)
+{
+ struct stat st;
+
+ if (overflow)
+ *overflow = 0;
+
+ if( a->directfp ) {
+ FILE *fp = a->directfp;
+
+ if( !fstat(fileno(fp), &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
+ return 0;
+ }
+
+ /* Hmmm: file_filter may have already been removed */
+ for( ; a; a = a->chain )
+ if( !a->chain && a->filter == file_filter ) {
+ file_filter_ctx_t *b = a->filter_ov;
+ FILEP_OR_FD fp = b->fp;
+
+#if defined(HAVE_DOSISH_SYSTEM) && !defined(FILE_FILTER_USES_STDIO)
+ ulong size;
+ static int (* __stdcall get_file_size_ex)
+ (void *handle, LARGE_INTEGER *size);
+ static int get_file_size_ex_initialized;
+
+ if (!get_file_size_ex_initialized)
+ {
+ void *handle;
+
+ handle = dlopen ("kernel32.dll", RTLD_LAZY);
+ if (handle)
+ {
+ get_file_size_ex = dlsym (handle, "GetFileSizeEx");
+ if (!get_file_size_ex)
+ dlclose (handle);
+ }
+ get_file_size_ex_initialized = 1;
+ }
+
+ if (get_file_size_ex)
+ {
+ /* This is a newer system with GetFileSizeEx; we use
+ this then becuase it seem that GetFileSize won't
+ return a proper error in case a file is larger than
+ 4GB. */
+ LARGE_INTEGER size;
+
+ if (get_file_size_ex (fp, &size))
+ {
+ if (!size.u.HighPart)
+ return size.u.LowPart;
+ if (overflow)
+ *overflow = 1;
+ return 0;
+ }
+ }
+ else
+ {
+ if ((size=GetFileSize (fp, NULL)) != 0xffffffff)
+ return size;
+ }
+ log_error ("GetFileSize for handle %p failed: %s\n",
+ fp, w32_strerror (0));
+#else
+ if( !fstat(my_fileno(fp), &st) )
+ return st.st_size;
+ log_error("fstat() failed: %s\n", strerror(errno) );
+#endif
+ break;
+ }
+
+ return 0;
+}
+
+
+/* Return the file descriptor of the underlying file or -1 if it is
+ not available. */
+int
+iobuf_get_fd (iobuf_t a)
+{
+ if (a->directfp)
+ return fileno ( (FILE*)a->directfp );
+
+ for ( ; a; a = a->chain )
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ FILEP_OR_FD fp = b->fp;
+
+ return my_fileno (fp);
+ }
+
+ return -1;
+}
+
+
+
+/****************
+ * Tell the file position, where the next read will take place
+ */
+off_t
+iobuf_tell (iobuf_t a)
+{
+ return a->ntotal + a->nbytes;
+}
+
+
+#if !defined(HAVE_FSEEKO) && !defined(fseeko)
+
+#ifdef HAVE_LIMITS_H
+# include <limits.h>
+#endif
+#ifndef LONG_MAX
+# define LONG_MAX ((long) ((unsigned long) -1 >> 1))
+#endif
+#ifndef LONG_MIN
+# define LONG_MIN (-1 - LONG_MAX)
+#endif
+
+/****************
+ * A substitute for fseeko, for hosts that don't have it.
+ */
+static int
+fseeko (FILE * stream, off_t newpos, int whence)
+{
+ while (newpos != (long) newpos)
+ {
+ long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
+ if (fseek (stream, pos, whence) != 0)
+ return -1;
+ newpos -= pos;
+ whence = SEEK_CUR;
+ }
+ return fseek (stream, (long) newpos, whence);
+}
+#endif
+
+/****************
+ * This is a very limited implementation. It simply discards all internal
+ * buffering and removes all filters but the first one.
+ */
+int
+iobuf_seek (iobuf_t a, off_t newpos)
+{
+ file_filter_ctx_t *b = NULL;
+
+ if (a->directfp)
+ {
+ FILE *fp = a->directfp;
+ if (fseeko (fp, newpos, SEEK_SET))
+ {
+ log_error ("can't seek: %s\n", strerror (errno));
+ return -1;
+ }
+ clearerr (fp);
+ }
+ else
+ {
+ for (; a; a = a->chain)
+ {
+ if (!a->chain && a->filter == file_filter)
+ {
+ b = a->filter_ov;
+ break;
+ }
+ }
+ if (!a)
+ return -1;
+#ifdef FILE_FILTER_USES_STDIO
+ if (fseeko (b->fp, newpos, SEEK_SET))
+ {
+ log_error ("can't fseek: %s\n", strerror (errno));
+ return -1;
+ }
+#else
+#ifdef HAVE_DOSISH_SYSTEM
+ if (SetFilePointer (b->fp, newpos, NULL, FILE_BEGIN) == 0xffffffff)
+ {
+ log_error ("SetFilePointer failed on handle %p: ec=%d\n",
+ b->fp, (int) GetLastError ());
+ return -1;
+ }
+#else
+ if (lseek (b->fp, newpos, SEEK_SET) == (off_t) - 1)
+ {
+ log_error ("can't lseek: %s\n", strerror (errno));
+ return -1;
+ }
+#endif
+#endif
+ }
+ a->d.len = 0; /* discard buffer */
+ a->d.start = 0;
+ a->nbytes = 0;
+ a->nlimit = 0;
+ a->nofast &= ~1;
+ a->ntotal = newpos;
+ a->error = 0;
+ /* remove filters, but the last */
+ if (a->chain)
+ log_debug ("pop_filter called in iobuf_seek - please report\n");
+ while (a->chain)
+ pop_filter (a, a->filter, NULL);
+
+ return 0;
+}
+
+
+
+
+
+
+/****************
+ * Retrieve the real filename
+ */
+const char *
+iobuf_get_real_fname (iobuf_t a)
+{
+ if (a->real_fname)
+ return a->real_fname;
+
+ /* the old solution */
+ for (; a; a = a->chain)
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ return b->print_only_name ? NULL : b->fname;
+ }
+
+ return NULL;
+}
+
+
+/****************
+ * Retrieve the filename
+ */
+const char *
+iobuf_get_fname (iobuf_t a)
+{
+ for (; a; a = a->chain)
+ if (!a->chain && a->filter == file_filter)
+ {
+ file_filter_ctx_t *b = a->filter_ov;
+ return b->fname;
+ }
+
+ return NULL;
+}
+
+
+/****************
+ * enable partial block mode as described in the OpenPGP draft.
+ * LEN is the first length byte on read, but ignored on writes.
+ */
+void
+iobuf_set_partial_block_mode (iobuf_t a, size_t len)
+{
+ block_filter_ctx_t *ctx = xcalloc (1, sizeof *ctx);
+
+ assert (a->use == 1 || a->use == 2);
+ ctx->use = a->use;
+ if (!len)
+ {
+ if (a->use == 1)
+ log_debug ("pop_filter called in set_partial_block_mode"
+ " - please report\n");
+ pop_filter (a, block_filter, NULL);
+ }
+ else
+ {
+ ctx->partial = 1;
+ ctx->size = 0;
+ ctx->first_c = len;
+ iobuf_push_filter (a, block_filter, ctx);
+ }
+}
+
+
+
+/****************
+ * Same as fgets() but if the buffer is too short a larger one will
+ * be allocated up to some limit *max_length.
+ * A line is considered a byte stream ending in a LF.
+ * Returns the length of the line. EOF is indicated by a line of
+ * length zero. The last LF may be missing due to an EOF.
+ * is max_length is zero on return, the line has been truncated.
+ *
+ * Note: The buffer is allocated with enough space to append a CR,LF,EOL
+ */
+unsigned int
+iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
+ unsigned *length_of_buffer, unsigned *max_length)
+{
+ int c;
+ char *buffer = (char *)*addr_of_buffer;
+ unsigned length = *length_of_buffer;
+ unsigned nbytes = 0;
+ unsigned maxlen = *max_length;
+ char *p;
+
+ if (!buffer)
+ { /* must allocate a new buffer */
+ length = 256;
+ buffer = xmalloc (length);
+ *addr_of_buffer = (unsigned char *)buffer;
+ *length_of_buffer = length;
+ }
+
+ length -= 3; /* reserve 3 bytes (cr,lf,eol) */
+ p = buffer;
+ while ((c = iobuf_get (a)) != -1)
+ {
+ if (nbytes == length)
+ { /* increase the buffer */
+ if (length > maxlen)
+ { /* this is out limit */
+ /* skip the rest of the line */
+ while (c != '\n' && (c = iobuf_get (a)) != -1)
+ ;
+ *p++ = '\n'; /* always append a LF (we have reserved space) */
+ nbytes++;
+ *max_length = 0; /* indicate truncation */
+ break;
+ }
+ length += 3; /* correct for the reserved byte */
+ length += length < 1024 ? 256 : 1024;
+ buffer = xrealloc (buffer, length);
+ *addr_of_buffer = (unsigned char *)buffer;
+ *length_of_buffer = length;
+ length -= 3; /* and reserve again */
+ p = buffer + nbytes;
+ }
+ *p++ = c;
+ nbytes++;
+ if (c == '\n')
+ break;
+ }
+ *p = 0; /* make sure the line is a string */
+
+ return nbytes;
+}
+
+/* This is the non iobuf specific function */
+int
+iobuf_translate_file_handle (int fd, int for_write)
+{
+#ifdef _WIN32
+ {
+ int x;
+
+ if (fd <= 2)
+ return fd; /* do not do this for error, stdin, stdout, stderr */
+
+ x = _open_osfhandle (fd, for_write ? 1 : 0);
+ if (x == -1)
+ log_error ("failed to translate osfhandle %p\n", (void *) fd);
+ else
+ {
+ /*log_info ("_open_osfhandle %p yields %d%s\n",
+ (void*)fd, x, for_write? " for writing":"" ); */
+ fd = x;
+ }
+ }
+#endif
+ return fd;
+}
+
+static int
+translate_file_handle (int fd, int for_write)
+{
+#ifdef _WIN32
+#ifdef FILE_FILTER_USES_STDIO
+ fd = iobuf_translate_file_handle (fd, for_write);
+#else
+ {
+ int x;
+
+ if (fd == 0)
+ x = (int) GetStdHandle (STD_INPUT_HANDLE);
+ else if (fd == 1)
+ x = (int) GetStdHandle (STD_OUTPUT_HANDLE);
+ else if (fd == 2)
+ x = (int) GetStdHandle (STD_ERROR_HANDLE);
+ else
+ x = fd;
+
+ if (x == -1)
+ log_debug ("GetStdHandle(%d) failed: ec=%d\n",
+ fd, (int) GetLastError ());
+
+ fd = x;
+ }
+#endif
+#endif
+ return fd;
+}
+
+
+void
+iobuf_skip_rest (iobuf_t a, unsigned long n, int partial)
+{
+ if ( partial )
+ {
+ for (;;)
+ {
+ if (a->nofast || a->d.start >= a->d.len)
+ {
+ if (iobuf_readbyte (a) == -1)
+ {
+ break;
+ }
+ }
+ else
+ {
+ unsigned long count = a->d.len - a->d.start;
+ a->nbytes += count;
+ a->d.start = a->d.len;
+ }
+ }
+ }
+ else
+ {
+ unsigned long remaining = n;
+ while (remaining > 0)
+ {
+ if (a->nofast || a->d.start >= a->d.len)
+ {
+ if (iobuf_readbyte (a) == -1)
+ {
+ break;
+ }
+ --remaining;
+ }
+ else
+ {
+ unsigned long count = a->d.len - a->d.start;
+ if (count > remaining)
+ {
+ count = remaining;
+ }
+ a->nbytes += count;
+ a->d.start += count;
+ remaining -= count;
+ }
+ }
+ }
+}
diff --git a/common/iobuf.h b/common/iobuf.h
new file mode 100644
index 000000000..a3dd7f1c5
--- /dev/null
+++ b/common/iobuf.h
@@ -0,0 +1,174 @@
+/* iobuf.h - I/O buffer
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_IOBUF_H
+#define GNUPG_COMMON_IOBUF_H
+
+#include "../include/types.h" /* fixme: should be moved elsewhere. */
+
+
+#define DBG_IOBUF iobuf_debug_mode
+
+
+#define IOBUFCTRL_INIT 1
+#define IOBUFCTRL_FREE 2
+#define IOBUFCTRL_UNDERFLOW 3
+#define IOBUFCTRL_FLUSH 4
+#define IOBUFCTRL_DESC 5
+#define IOBUFCTRL_CANCEL 6
+#define IOBUFCTRL_USER 16
+
+typedef struct iobuf_struct *iobuf_t;
+typedef struct iobuf_struct *IOBUF; /* Compatibility with gpg 1.4. */
+
+/* fixme: we should hide most of this stuff */
+struct iobuf_struct
+{
+ int use; /* 1 input , 2 output, 3 temp */
+ off_t nlimit;
+ off_t nbytes; /* used together with nlimit */
+ off_t ntotal; /* total bytes read (position of stream) */
+ int nofast; /* used by the iobuf_get() */
+ void *directfp;
+ struct
+ {
+ size_t size; /* allocated size */
+ size_t start; /* number of invalid bytes at the begin of the buffer */
+ size_t len; /* currently filled to this size */
+ byte *buf;
+ }
+ d;
+ int filter_eof;
+ int error;
+ int (*filter) (void *opaque, int control,
+ iobuf_t chain, byte * buf, size_t * len);
+ void *filter_ov; /* value for opaque */
+ int filter_ov_owner;
+ char *real_fname;
+ iobuf_t chain; /* next iobuf used for i/o if any
+ (passed to filter) */
+ int no, subno;
+ const char *desc;
+ void *opaque; /* can be used to hold any information
+ this value is copied to all
+ instances */
+ struct
+ {
+ size_t size; /* allocated size */
+ size_t start; /* number of invalid bytes at the
+ begin of the buffer */
+ size_t len; /* currently filled to this size */
+ byte *buf;
+ }
+ unget;
+};
+
+#ifndef EXTERN_UNLESS_MAIN_MODULE
+#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
+#define EXTERN_UNLESS_MAIN_MODULE extern
+#else
+#define EXTERN_UNLESS_MAIN_MODULE
+#endif
+#endif
+EXTERN_UNLESS_MAIN_MODULE int iobuf_debug_mode;
+
+void iobuf_enable_special_filenames (int yes);
+int iobuf_is_pipe_filename (const char *fname);
+iobuf_t iobuf_alloc (int use, size_t bufsize);
+iobuf_t iobuf_temp (void);
+iobuf_t iobuf_temp_with_content (const char *buffer, size_t length);
+iobuf_t iobuf_open (const char *fname);
+iobuf_t iobuf_fdopen (int fd, const char *mode);
+iobuf_t iobuf_sockopen (int fd, const char *mode);
+iobuf_t iobuf_create (const char *fname);
+iobuf_t iobuf_append (const char *fname);
+iobuf_t iobuf_openrw (const char *fname);
+int iobuf_ioctl (iobuf_t a, int cmd, int intval, void *ptrval);
+int iobuf_close (iobuf_t iobuf);
+int iobuf_cancel (iobuf_t iobuf);
+
+int iobuf_push_filter (iobuf_t a, int (*f) (void *opaque, int control,
+ iobuf_t chain, byte * buf,
+ size_t * len), void *ov);
+int iobuf_push_filter2 (iobuf_t a,
+ int (*f) (void *opaque, int control, iobuf_t chain,
+ byte * buf, size_t * len), void *ov,
+ int rel_ov);
+int iobuf_flush (iobuf_t a);
+void iobuf_clear_eof (iobuf_t a);
+#define iobuf_set_error(a) do { (a)->error = 1; } while(0)
+#define iobuf_error(a) ((a)->error)
+
+void iobuf_set_limit (iobuf_t a, off_t nlimit);
+
+off_t iobuf_tell (iobuf_t a);
+int iobuf_seek (iobuf_t a, off_t newpos);
+
+int iobuf_readbyte (iobuf_t a);
+int iobuf_read (iobuf_t a, void *buf, unsigned buflen);
+unsigned iobuf_read_line (iobuf_t a, byte ** addr_of_buffer,
+ unsigned *length_of_buffer, unsigned *max_length);
+int iobuf_peek (iobuf_t a, byte * buf, unsigned buflen);
+int iobuf_writebyte (iobuf_t a, unsigned c);
+int iobuf_write (iobuf_t a, const void *buf, unsigned buflen);
+int iobuf_writestr (iobuf_t a, const char *buf);
+
+void iobuf_flush_temp (iobuf_t temp);
+int iobuf_write_temp (iobuf_t a, iobuf_t temp);
+size_t iobuf_temp_to_buffer (iobuf_t a, byte * buffer, size_t buflen);
+void iobuf_unget_and_close_temp (iobuf_t a, iobuf_t temp);
+
+off_t iobuf_get_filelength (iobuf_t a, int *overflow);
+#define IOBUF_FILELENGTH_LIMIT 0xffffffff
+int iobuf_get_fd (iobuf_t a);
+const char *iobuf_get_real_fname (iobuf_t a);
+const char *iobuf_get_fname (iobuf_t a);
+
+void iobuf_set_partial_block_mode (iobuf_t a, size_t len);
+
+int iobuf_translate_file_handle (int fd, int for_write);
+
+void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial);
+
+
+/* get a byte form the iobuf; must check for eof prior to this function
+ * this function returns values in the range 0 .. 255 or -1 to indicate EOF
+ * iobuf_get_noeof() does not return -1 to indicate EOF, but masks the
+ * returned value to be in the range 0 ..255.
+ */
+#define iobuf_get(a) \
+ ( ((a)->nofast || (a)->d.start >= (a)->d.len )? \
+ iobuf_readbyte((a)) : ( (a)->nbytes++, (a)->d.buf[(a)->d.start++] ) )
+#define iobuf_get_noeof(a) (iobuf_get((a))&0xff)
+
+/* write a byte to the iobuf and return true on write error
+ * This macro does only write the low order byte
+ */
+#define iobuf_put(a,c) iobuf_writebyte(a,c)
+
+#define iobuf_where(a) "[don't know]"
+#define iobuf_id(a) ((a)->no)
+
+#define iobuf_get_temp_buffer(a) ( (a)->d.buf )
+#define iobuf_get_temp_length(a) ( (a)->d.len )
+#define iobuf_is_temp(a) ( (a)->use == 3 )
+
+#endif /*GNUPG_COMMON_IOBUF_H*/
diff --git a/common/isascii.c b/common/isascii.c
new file mode 100644
index 000000000..b71febe99
--- /dev/null
+++ b/common/isascii.c
@@ -0,0 +1,30 @@
+/* isascii.c - Replacement for isascii.
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+int
+isascii (int c)
+{
+ return (((c) & ~0x7f) == 0);
+}
diff --git a/common/maperror.c b/common/maperror.c
new file mode 100644
index 000000000..06546b501
--- /dev/null
+++ b/common/maperror.c
@@ -0,0 +1,105 @@
+/* maperror.c - Error mapping
+ * Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <assuan.h>
+
+#include "util.h"
+#include "errors.h"
+
+
+/* Map Assuan error code ERR to an GPG_ERR_ code. We need to
+ distinguish between genuine (and legacy) Assuan error codes and
+ application error codes shared with all GnuPG modules. The rule is
+ simple: All errors with a gpg_err_source of UNKNOWN are genuine
+ Assuan codes all others are passed verbatim through. */
+gpg_error_t
+map_assuan_err_with_source (int source, int err)
+{
+ gpg_err_code_t ec;
+
+ if (gpg_err_source (err))
+ return err;
+
+ switch (err)
+ {
+ case -1: ec = GPG_ERR_EOF; break;
+ case 0: ec = 0; break;
+
+ case ASSUAN_Canceled: ec = GPG_ERR_CANCELED; break;
+ case ASSUAN_Invalid_Index: ec = GPG_ERR_INV_INDEX; break;
+
+ case ASSUAN_Not_Implemented: ec = GPG_ERR_NOT_IMPLEMENTED; break;
+ case ASSUAN_Server_Fault: ec = GPG_ERR_ASSUAN_SERVER_FAULT; break;
+ case ASSUAN_No_Public_Key: ec = GPG_ERR_NO_PUBKEY; break;
+ case ASSUAN_No_Secret_Key: ec = GPG_ERR_NO_SECKEY; break;
+
+ case ASSUAN_Cert_Revoked: ec = GPG_ERR_CERT_REVOKED; break;
+ case ASSUAN_No_CRL_For_Cert: ec = GPG_ERR_NO_CRL_KNOWN; break;
+ case ASSUAN_CRL_Too_Old: ec = GPG_ERR_CRL_TOO_OLD; break;
+
+ case ASSUAN_Not_Trusted: ec = GPG_ERR_NOT_TRUSTED; break;
+
+ case ASSUAN_Card_Error: ec = GPG_ERR_CARD; break;
+ case ASSUAN_Invalid_Card: ec = GPG_ERR_INV_CARD; break;
+ case ASSUAN_No_PKCS15_App: ec = GPG_ERR_NO_PKCS15_APP; break;
+ case ASSUAN_Card_Not_Present: ec= GPG_ERR_CARD_NOT_PRESENT; break;
+ case ASSUAN_Not_Confirmed: ec = GPG_ERR_NOT_CONFIRMED; break;
+ case ASSUAN_Invalid_Id: ec = GPG_ERR_INV_ID; break;
+
+ case ASSUAN_Locale_Problem: ec = GPG_ERR_LOCALE_PROBLEM; break;
+
+ default:
+ ec = err < 100? GPG_ERR_ASSUAN_SERVER_FAULT : GPG_ERR_ASSUAN;
+ break;
+ }
+ return gpg_err_make (source, ec);
+}
+
+/* Map GPG_xERR_xx error codes to Assuan status codes */
+int
+map_to_assuan_status (int rc)
+{
+ gpg_err_code_t ec = gpg_err_code (rc);
+ gpg_err_source_t es = gpg_err_source (rc);
+
+ if (!rc)
+ return 0;
+ if (!es)
+ {
+ es = GPG_ERR_SOURCE_USER_4; /* This should not happen, but we
+ need to make sure to pass a new
+ Assuan errorcode along. */
+ log_debug ("map_to_assuan_status called with no error source\n");
+ }
+
+ if (ec == -1)
+ ec = GPG_ERR_NO_DATA; /* That used to be ASSUAN_No_Data_Available. */
+
+ return gpg_err_make (es, ec);
+}
diff --git a/common/membuf.c b/common/membuf.c
new file mode 100644
index 000000000..2d35fefab
--- /dev/null
+++ b/common/membuf.c
@@ -0,0 +1,95 @@
+/* membuf.c - A simple implementation of a dynamic buffer
+ * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "membuf.h"
+
+#include "util.h"
+
+
+/* A simple implementation of a dynamic buffer. Use init_membuf() to
+ create a buffer, put_membuf to append bytes and get_membuf to
+ release and return the buffer. Allocation errors are detected but
+ only returned at the final get_membuf(), this helps not to clutter
+ the code with out of core checks. */
+
+void
+init_membuf (membuf_t *mb, int initiallen)
+{
+ mb->len = 0;
+ mb->size = initiallen;
+ mb->out_of_core = 0;
+ mb->buf = xtrymalloc (initiallen);
+ if (!mb->buf)
+ mb->out_of_core = errno;
+}
+
+
+void
+put_membuf (membuf_t *mb, const void *buf, size_t len)
+{
+ if (mb->out_of_core)
+ return;
+
+ if (mb->len + len >= mb->size)
+ {
+ char *p;
+
+ mb->size += len + 1024;
+ p = xtryrealloc (mb->buf, mb->size);
+ if (!p)
+ {
+ mb->out_of_core = errno;
+ /* Wipe out what we already accumulated. This is required
+ in case we are storing sensitive data here. The membuf
+ API does not provide another way to cleanup after an
+ error. */
+ memset (mb->buf, 0, mb->len);
+ return;
+ }
+ mb->buf = p;
+ }
+ memcpy (mb->buf + mb->len, buf, len);
+ mb->len += len;
+}
+
+
+void *
+get_membuf (membuf_t *mb, size_t *len)
+{
+ char *p;
+
+ if (mb->out_of_core)
+ {
+ xfree (mb->buf);
+ mb->buf = NULL;
+ return NULL;
+ }
+
+ p = mb->buf;
+ *len = mb->len;
+ mb->buf = NULL;
+ mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
+ return p;
+}
diff --git a/common/membuf.h b/common/membuf.h
new file mode 100644
index 000000000..9033be61e
--- /dev/null
+++ b/common/membuf.h
@@ -0,0 +1,42 @@
+/* membuf.h - A simple implementation of a dynamic buffer
+ * Copyright (C) 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_MEMBUF_H
+#define GNUPG_COMMON_MEMBUF_H
+
+/* The definition of the structure is private, we only need it here,
+ so it can be allocated on the stack. */
+struct private_membuf_s {
+ size_t len;
+ size_t size;
+ char *buf;
+ int out_of_core;
+};
+
+typedef struct private_membuf_s membuf_t;
+
+
+void init_membuf (membuf_t *mb, int initiallen);
+void put_membuf (membuf_t *mb, const void *buf, size_t len);
+void *get_membuf (membuf_t *mb, size_t *len);
+
+
+#endif /*GNUPG_COMMON_MEMBUF_H*/
diff --git a/common/miscellaneous.c b/common/miscellaneous.c
new file mode 100644
index 000000000..da74f65bc
--- /dev/null
+++ b/common/miscellaneous.c
@@ -0,0 +1,151 @@
+/* miscellaneous.c - Stuff not fitting elsewhere
+ * Copyright (C) 2003, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "util.h"
+#include "iobuf.h"
+
+
+/* Decide whether the filename is stdout or a real filename and return
+ * an appropriate string. */
+const char *
+print_fname_stdout (const char *s)
+{
+ if( !s || (*s == '-' && !s[1]) )
+ return "[stdout]";
+ return s;
+}
+
+
+/* Decide whether the filename is stdin or a real filename and return
+ * an appropriate string. */
+const char *
+print_fname_stdin (const char *s)
+{
+ if( !s || (*s == '-' && !s[1]) )
+ return "[stdin]";
+ return s;
+}
+
+/* fixme: Globally replace it by print_sanitized_buffer. */
+void
+print_string( FILE *fp, const byte *p, size_t n, int delim )
+{
+ print_sanitized_buffer (fp, p, n, delim);
+}
+
+void
+print_utf8_string2 ( FILE *fp, const byte *p, size_t n, int delim )
+{
+ print_sanitized_utf8_buffer (fp, p, n, delim);
+}
+
+void
+print_utf8_string( FILE *fp, const byte *p, size_t n )
+{
+ print_utf8_string2 (fp, p, n, 0);
+}
+
+char *
+make_printable_string (const void *p, size_t n, int delim )
+{
+ return sanitize_buffer (p, n, delim);
+}
+
+
+/*
+ * Check if the file is compressed.
+ */
+int
+is_file_compressed (const char *s, int *ret_rc)
+{
+ iobuf_t a;
+ byte buf[4];
+ int i, rc = 0;
+ int overflow;
+
+ struct magic_compress_s {
+ size_t len;
+ byte magic[4];
+ } magic[] = {
+ { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
+ { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
+ { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
+ };
+
+ if ( iobuf_is_pipe_filename (s) || !ret_rc )
+ return 0; /* We can't check stdin or no file was given */
+
+ a = iobuf_open( s );
+ if ( a == NULL ) {
+ *ret_rc = gpg_error_from_errno (errno);
+ return 0;
+ }
+
+ if ( iobuf_get_filelength( a, &overflow ) < 4 && !overflow) {
+ *ret_rc = 0;
+ goto leave;
+ }
+
+ if ( iobuf_read( a, buf, 4 ) == -1 ) {
+ *ret_rc = a->error;
+ goto leave;
+ }
+
+ for ( i = 0; i < DIM( magic ); i++ ) {
+ if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) {
+ *ret_rc = 0;
+ rc = 1;
+ break;
+ }
+ }
+
+leave:
+ iobuf_close( a );
+ return rc;
+}
+
+
+/* Try match against each substring of multistr, delimited by | */
+int
+match_multistr (const char *multistr,const char *match)
+{
+ do
+ {
+ size_t seglen = strcspn (multistr,"|");
+ if (!seglen)
+ break;
+ /* Using the localized strncasecmp! */
+ if (strncasecmp(multistr,match,seglen)==0)
+ return 1;
+ multistr += seglen;
+ if (*multistr == '|')
+ multistr++;
+ }
+ while (*multistr);
+
+ return 0;
+}
+
+
diff --git a/common/mkerrors b/common/mkerrors
new file mode 100755
index 000000000..994c61352
--- /dev/null
+++ b/common/mkerrors
@@ -0,0 +1,73 @@
+#!/bin/sh
+# mkerrors - Extract error strings from errors.h
+# and create C source for gnupg_strerror
+# Copyright (C) 2001 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+cat <<EOF
+/* Generated automatically by mkerrors */
+/* Do not edit! */
+
+#include <config.h>
+#include <stdio.h>
+#include "errors.h"
+
+/**
+ * gnupg_strerror:
+ * @err: Error code
+ *
+ * This function returns a textual representaion of the given
+ * errorcode. If this is an unknown value, a string with the value
+ * is returned (Beware: it is hold in a static buffer).
+ *
+ * Return value: String with the error description.
+ **/
+const char *
+gnupg_strerror (int err)
+{
+ const char *s;
+ static char buf[25];
+
+ switch (err)
+ {
+EOF
+
+awk '
+/GNUPG_No_Error/ { okay=1 }
+!okay {next}
+/}/ { exit 0 }
+/GNUPG_[A-Za-z_]*/ { print_code($1) }
+
+
+function print_code( s )
+{
+printf " case %s: s=\"", s ;
+gsub(/_/, " ", s );
+printf "%s\"; break;\n", tolower(substr(s,7));
+}
+'
+
+cat <<EOF
+ default: sprintf (buf, "ec=%d", err ); s=buf; break;
+ }
+
+ return s;
+}
+
+EOF \ No newline at end of file
diff --git a/common/mkerrtok b/common/mkerrtok
new file mode 100755
index 000000000..67ab42c89
--- /dev/null
+++ b/common/mkerrtok
@@ -0,0 +1,68 @@
+#!/bin/sh
+# mkerrtok - Create error tokens from errors.h
+# and the C source for gnupg_errortoken
+# Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+#
+# This file is part of GnuPG.
+#
+# GnuPG is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GnuPG 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+cat <<EOF
+/* Generated automatically by mkerrtok */
+/* Do not edit! */
+
+/**
+ * gnupg_error_token:
+ * @err: Error code
+ *
+ * This function returns a textual representaion of the given
+ * errorcode. If this is an unknown value, a static string is returned.
+ * This function differs from gnupg_strerror that it yields the string
+ * representation of the macro which is never subject to i18n.
+ *
+ * Return value: String with the error token.
+ **/
+const char *
+gnupg_error_token (int err)
+{
+ const char *s;
+
+ switch (err)
+ {
+EOF
+
+awk '
+/GNUPG_No_Error/ { okay=1 }
+!okay {next}
+/}/ { exit 0 }
+/GNUPG_[A-Za-z_]*/ { print_code($1) }
+
+
+function print_code( s )
+{
+printf " case %s: s=\"", s ;
+printf "%s\"; break;\n", substr(s,7);
+}
+'
+
+cat <<EOF
+ default: s = "Unknown_Error"; break;
+ }
+
+ return s;
+}
+
+EOF
diff --git a/common/pka.c b/common/pka.c
new file mode 100644
index 000000000..3d442d16a
--- /dev/null
+++ b/common/pka.c
@@ -0,0 +1,252 @@
+/* pka.c - DNS Public Key Association RR access
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef USE_DNS_PKA
+#include <sys/types.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+#endif /* USE_DNS_PKA */
+
+#include "util.h"
+#include "pka.h"
+
+#ifdef USE_DNS_PKA
+/* Parse the TXT resource record. Format is:
+
+ v=pka1;fpr=a4d94e92b0986ab5ee9dcd755de249965b0358a2;uri=string
+
+ For simplicity white spaces are not allowed. Because we expect to
+ use a new RRTYPE for this in the future we define the TXT really
+ strict for simplicity: No white spaces, case sensitivity of the
+ names, order must be as given above. Only URI is optional.
+
+ This function modifies BUFFER. On success 0 is returned, the 20
+ byte fingerprint stored at FPR and BUFFER contains the URI or an
+ empty string.
+*/
+static int
+parse_txt_record (char *buffer, unsigned char *fpr)
+{
+ char *p, *pend;
+ int i;
+
+ p = buffer;
+ pend = strchr (p, ';');
+ if (!pend)
+ return -1;
+ *pend++ = 0;
+ if (strcmp (p, "v=pka1"))
+ return -1; /* Wrong or missing version. */
+
+ p = pend;
+ pend = strchr (p, ';');
+ if (pend)
+ *pend++ = 0;
+ if (strncmp (p, "fpr=", 4))
+ return -1; /* Missing fingerprint part. */
+ p += 4;
+ for (i=0; i < 20 && hexdigitp (p) && hexdigitp (p+1); i++, p += 2)
+ fpr[i] = xtoi_2 (p);
+ if (i != 20)
+ return -1; /* Fingerprint consists not of exactly 40 hexbytes. */
+
+ p = pend;
+ if (!p || !*p)
+ {
+ *buffer = 0;
+ return 0; /* Success (no URI given). */
+ }
+ if (strncmp (p, "uri=", 4))
+ return -1; /* Unknown part. */
+ p += 4;
+ /* There is an URI, copy it to the start of the buffer. */
+ while (*p)
+ *buffer++ = *p++;
+ *buffer = 0;
+ return 0;
+}
+
+
+/* For the given email ADDRESS lookup the PKA information in the DNS.
+
+ On success the 20 byte SHA-1 fingerprint is stored at FPR and the
+ URI will be returned in an allocated buffer. Note that the URI
+ might be an zero length string as this information is optiobnal.
+ Caller must xfree the returned string.
+
+ On error NULL is returned and the 20 bytes at FPR are not
+ defined. */
+char *
+get_pka_info (const char *address, unsigned char *fpr)
+{
+ unsigned char answer[PACKETSZ];
+ int anslen;
+ int qdcount, ancount, nscount, arcount;
+ int rc;
+ unsigned char *p, *pend;
+ const char *domain;
+ char *name;
+
+
+ domain = strrchr (address, '@');
+ if (!domain || domain == address || !domain[1])
+ return NULL; /* invalid mail address given. */
+
+ name = malloc (strlen (address) + 5 + 1);
+ memcpy (name, address, domain - address);
+ strcpy (stpcpy (name + (domain-address), "._pka."), domain+1);
+
+ anslen = res_query (name, C_IN, T_TXT, answer, PACKETSZ);
+ xfree (name);
+ if (anslen < sizeof(HEADER))
+ return NULL; /* DNS resolver returned a too short answer. */
+ if ( (rc=((HEADER*)answer)->rcode) != NOERROR )
+ return NULL; /* DNS resolver returned an error. */
+
+ /* We assume that PACKETSZ is large enough and don't do dynmically
+ expansion of the buffer. */
+ if (anslen > PACKETSZ)
+ return NULL; /* DNS resolver returned a too long answer */
+
+ qdcount = ntohs (((HEADER*)answer)->qdcount);
+ ancount = ntohs (((HEADER*)answer)->ancount);
+ nscount = ntohs (((HEADER*)answer)->nscount);
+ arcount = ntohs (((HEADER*)answer)->arcount);
+
+ if (!ancount)
+ return NULL; /* Got no answer. */
+
+ p = answer + sizeof (HEADER);
+ pend = answer + anslen; /* Actually points directly behind the buffer. */
+
+ while (qdcount-- && p < pend)
+ {
+ rc = dn_skipname (p, pend);
+ if (rc == -1)
+ return NULL;
+ p += rc + QFIXEDSZ;
+ }
+
+ if (ancount > 1)
+ return NULL; /* more than one possible gpg trustdns record - none used. */
+
+ while (ancount-- && p <= pend)
+ {
+ unsigned int type, class, txtlen, n;
+ char *buffer, *bufp;
+
+ rc = dn_skipname (p, pend);
+ if (rc == -1)
+ return NULL;
+ p += rc;
+ if (p >= pend - 10)
+ return NULL; /* RR too short. */
+
+ type = *p++ << 8;
+ type |= *p++;
+ class = *p++ << 8;
+ class |= *p++;
+ p += 4;
+ txtlen = *p++ << 8;
+ txtlen |= *p++;
+ if (type != T_TXT || class != C_IN)
+ return NULL; /* Answer does not match the query. */
+
+ buffer = bufp = xmalloc (txtlen + 1);
+ while (txtlen && p < pend)
+ {
+ for (n = *p++, txtlen--; txtlen && n && p < pend; txtlen--, n--)
+ *bufp++ = *p++;
+ }
+ *bufp = 0;
+ if (parse_txt_record (buffer, fpr))
+ {
+ xfree (buffer);
+ return NULL; /* Not a valid gpg trustdns RR. */
+ }
+ return buffer;
+ }
+
+ return NULL;
+}
+#else /* !USE_DNS_PKA */
+
+/* Dummy version of the function if we can't use the resolver
+ functions. */
+char *
+get_pka_info (const char *address, unsigned char *fpr)
+{
+ return NULL;
+}
+#endif /* !USE_DNS_PKA */
+
+
+#ifdef TEST
+int
+main(int argc,char *argv[])
+{
+ unsigned char fpr[20];
+ char *uri;
+ int i;
+
+ if (argc < 2)
+ {
+ fprintf (stderr, "usage: pka mail-addresses\n");
+ return 1;
+ }
+ argc--;
+ argv++;
+
+ for (; argc; argc--, argv++)
+ {
+ uri = get_pka_info ( *argv, fpr );
+ printf ("%s", *argv);
+ if (uri)
+ {
+ putchar (' ');
+ for (i=0; i < 20; i++)
+ printf ("%02X", fpr[i]);
+ if (*uri)
+ printf (" %s", uri);
+ xfree (uri);
+ }
+ putchar ('\n');
+ }
+ return 0;
+}
+#endif /* TEST */
+
+/*
+Local Variables:
+compile-command: "cc -DUSE_DNS_PKA -DTEST -I.. -I../include -Wall -g -o pka pka.c -lresolv libutil.a"
+End:
+*/
diff --git a/common/pka.h b/common/pka.h
new file mode 100644
index 000000000..d0b977d0f
--- /dev/null
+++ b/common/pka.h
@@ -0,0 +1,27 @@
+/* pka.h - DNS Public Key Association RR access definitions
+ * Copyright (C) 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef GNUPG_COMMON_PKA_H
+#define GNUPG_COMMON_PKA_H
+
+char *get_pka_info (const char *address, unsigned char *fpr);
+
+
+#endif /*GNUPG_COMMON_PKA_H*/
diff --git a/common/sexp-parse.h b/common/sexp-parse.h
new file mode 100644
index 000000000..1064e51c4
--- /dev/null
+++ b/common/sexp-parse.h
@@ -0,0 +1,100 @@
+/* sexp-parse.h - S-Exp helper functions
+ * Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef SEXP_PARSE_H
+#define SEXP_PARSE_H
+
+#include <gpg-error.h>
+
+/* Return the length of the next S-Exp part and update the pointer to
+ the first data byte. 0 is returned on error */
+static inline size_t
+snext (unsigned char const **buf)
+{
+ const unsigned char *s;
+ int n;
+
+ s = *buf;
+ for (n=0; *s && *s != ':' && (*s >= '0' && *s <= '9'); s++)
+ n = n*10 + (*s - '0');
+ if (!n || *s != ':')
+ return 0; /* we don't allow empty lengths */
+ *buf = s+1;
+ return n;
+}
+
+/* Skip over the S-Expression BUF points to and update BUF to point to
+ the chacter right behind. DEPTH gives the initial number of open
+ lists and may be passed as a positive number to skip over the
+ remainder of an S-Expression if the current position is somewhere
+ in an S-Expression. The function may return an error code if it
+ encounters an impossible conditions */
+static inline gpg_error_t
+sskip (unsigned char const **buf, int *depth)
+{
+ const unsigned char *s = *buf;
+ size_t n;
+ int d = *depth;
+
+ while (d > 0)
+ {
+ if (*s == '(')
+ {
+ d++;
+ s++;
+ }
+ else if (*s == ')')
+ {
+ d--;
+ s++;
+ }
+ else
+ {
+ if (!d)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ n = snext (&s);
+ if (!n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s += n;
+ }
+ }
+ *buf = s;
+ *depth = d;
+ return 0;
+}
+
+
+/* Check whether the the string at the address BUF points to matches
+ the token. Return true on match and update BUF to point behind the
+ token. Return false and dont update tha buffer if it does not
+ match. */
+static inline int
+smatch (unsigned char const **buf, size_t buflen, const char *token)
+{
+ size_t toklen = strlen (token);
+
+ if (buflen != toklen || memcmp (*buf, token, toklen))
+ return 0;
+ *buf += toklen;
+ return 1;
+}
+
+#endif /*SEXP_PARSE_H*/
diff --git a/common/sexputil.c b/common/sexputil.c
new file mode 100644
index 000000000..fe0870c56
--- /dev/null
+++ b/common/sexputil.c
@@ -0,0 +1,145 @@
+/* sexputil.c - Utility fnctions for S-expressions.
+ * Copyright (C) 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* This file implements a few utility functions useful when working
+ with canonical encrypted S-expresions (i.e. not the S-exprssion
+ objects from libgcrypt). */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+
+#include "util.h"
+
+
+/* Return the so called "keygrip" which is the SHA-1 hash of the
+ public key parameters expressed in a way depended on the algorithm.
+
+ KEY is expected to be an canonical encoded S-expression with a
+ public or private key. KEYLEN is the length of that buffer.
+
+ GRIP must be at least 20 bytes long On success 0 is return, on
+ error an aerror code. */
+gpg_error_t
+keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
+ unsigned char *grip)
+{
+ gpg_error_t err;
+ gcry_sexp_t sexp;
+
+ if (!grip)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ err = gcry_sexp_sscan (&sexp, NULL, (const char *)key, keylen);
+ if (err)
+ return err;
+ if (!gcry_pk_get_keygrip (sexp, grip))
+ err = gpg_error (GPG_ERR_INTERNAL);
+ gcry_sexp_release (sexp);
+ return err;
+}
+
+
+/* Compare two simple S-expressions like "(3:foo)". Returns 0 if they
+ are identical or !0 if they are not. Not that this function can't
+ be used for sorting. */
+int
+cmp_simple_canon_sexp (const unsigned char *a_orig,
+ const unsigned char *b_orig)
+{
+ const char *a = (const char *)a_orig;
+ const char *b = (const char *)b_orig;
+ unsigned long n1, n2;
+ char *endp;
+
+ if (!a && !b)
+ return 0; /* Both are NULL, they are identical. */
+ if (!a || !b)
+ return 1; /* One is NULL, they are not identical. */
+ if (*a != '(' || *b != '(')
+ log_bug ("invalid S-exp in cmp_simple_canon_sexp\n");
+
+ a++;
+ n1 = strtoul (a, &endp, 10);
+ a = endp;
+ b++;
+ n2 = strtoul (b, &endp, 10);
+ b = endp;
+
+ if (*a != ':' || *b != ':' )
+ log_bug ("invalid S-exp in cmp_simple_canon_sexp\n");
+ if (n1 != n2)
+ return 1; /* Not the same. */
+
+ for (a++, b++; n1; n1--, a++, b++)
+ if (*a != *b)
+ return 1; /* Not the same. */
+ return 0;
+}
+
+
+/* Create a simple S-expression from the hex string at LIBNE. Returns
+ a newly allocated buffer with that canonical encoded S-expression
+ or NULL in case of an error. On return the number of characters
+ scanned in LINE will be stored at NSCANNED. This fucntions stops
+ converting at the first character not representing a hexdigit. Odd
+ numbers of hex digits are allowed; a leading zero is then
+ assumed. If no characters have been found, NULL is returned.*/
+unsigned char *
+make_simple_sexp_from_hexstr (const char *line, size_t *nscanned)
+{
+ size_t n, len;
+ const char *s;
+ unsigned char *buf;
+ unsigned char *p;
+ char numbuf[50];
+
+ for (n=0, s=line; hexdigitp (s); s++, n++)
+ ;
+ if (nscanned)
+ *nscanned = n;
+ if (!n)
+ return NULL;
+ len = ((n+1) & ~0x01)/2;
+ sprintf (numbuf, "(%u:", (unsigned int)len);
+ buf = xtrymalloc (strlen (numbuf) + len + 1 + 1);
+ if (!buf)
+ return NULL;
+ p = (unsigned char *)stpcpy ((char *)buf, numbuf);
+ s = line;
+ if ((n&1))
+ {
+ *p++ = xtoi_1 (s);
+ s++;
+ n--;
+ }
+ for (; n > 1; n -=2, s += 2)
+ *p++ = xtoi_2 (s);
+ *p++ = ')';
+ *p = 0; /* (Not really neaded.) */
+
+ return buf;
+}
diff --git a/common/signal.c b/common/signal.c
new file mode 100644
index 000000000..0c79214b2
--- /dev/null
+++ b/common/signal.c
@@ -0,0 +1,259 @@
+/* signal.c - signal handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "util.h"
+
+
+#ifndef HAVE_DOSISH_SYSTEM
+static volatile int caught_fatal_sig;
+static volatile int caught_sigusr1;
+#endif
+static void (*cleanup_fnc)(void);
+
+
+#ifndef HAVE_DOSISH_SYSTEM
+static void
+init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign )
+{
+# ifdef HAVE_SIGACTION
+ struct sigaction oact, nact;
+
+ if (check_ign)
+ {
+ /* we don't want to change an IGN handler */
+ sigaction (sig, NULL, &oact );
+ if (oact.sa_handler == SIG_IGN )
+ return;
+ }
+
+ nact.sa_handler = handler;
+ sigemptyset (&nact.sa_mask);
+ nact.sa_flags = 0;
+ sigaction ( sig, &nact, NULL);
+# else
+ RETSIGTYPE (*ohandler)(int);
+
+ ohandler = signal (sig, handler);
+ if (check_ign && ohandler == SIG_IGN)
+ {
+ /* Change it back if it was already set to IGN */
+ signal (sig, SIG_IGN);
+ }
+# endif
+}
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+#ifndef HAVE_DOSISH_SYSTEM
+static const char *
+get_signal_name( int signum )
+{
+ /* Note that we can't use strsignal(), because it is not
+ reentrant. */
+#if HAVE_DECL_SYS_SIGLIST && defined(NSIG)
+ return (signum >= 0 && signum < NSIG) ? sys_siglist[signum] : "?";
+#else
+ return NULL;
+#endif
+}
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+#ifndef HAVE_DOSISH_SYSTEM
+static RETSIGTYPE
+got_fatal_signal (int sig)
+{
+ const char *s;
+
+ if (caught_fatal_sig)
+ raise (sig);
+ caught_fatal_sig = 1;
+
+ if (cleanup_fnc)
+ cleanup_fnc ();
+ /* Better don't translate these messages. */
+ write (2, "\n", 1 );
+ s = log_get_prefix (NULL);
+ if (s)
+ write(2, s, strlen (s));
+ write (2, ": signal ", 9 );
+ s = get_signal_name(sig);
+ if (s)
+ write (2, s, strlen(s) );
+ else
+ {
+ /* We are in a signal handler so we can't use any kind of printf
+ even not sprintf. USe a straightforward algorithm. */
+ if (sig < 0 || sig >= 100000)
+ write (2, "?", 1);
+ else
+ {
+ int i, any=0;
+
+ for (i=10000; i; i /= 10)
+ {
+ if (sig >= i || ((any || i==1) && !(sig/i)))
+ {
+ write (2, "0123456789"+(sig/i), 1);
+ if ((sig/i))
+ any = 1;
+ sig %= i;
+ }
+ }
+ }
+ }
+ write (2, " caught ... exiting\n", 20);
+
+ /* Reset action to default action and raise signal again */
+ init_one_signal (sig, SIG_DFL, 0);
+ /* Fixme: remove_lockfiles ();*/
+#ifdef __riscos__
+ close_fds ();
+#endif /* __riscos__ */
+ raise( sig );
+}
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+#ifndef HAVE_DOSISH_SYSTEM
+static RETSIGTYPE
+got_usr_signal (int sig)
+{
+ caught_sigusr1 = 1;
+}
+#endif /*!HAVE_DOSISH_SYSTEM*/
+
+void
+gnupg_init_signals (int mode, void (*fast_cleanup)(void))
+{
+ assert (!mode);
+
+ cleanup_fnc = fast_cleanup;
+#ifndef HAVE_DOSISH_SYSTEM
+ init_one_signal (SIGINT, got_fatal_signal, 1 );
+ init_one_signal (SIGHUP, got_fatal_signal, 1 );
+ init_one_signal (SIGTERM, got_fatal_signal, 1 );
+ init_one_signal (SIGQUIT, got_fatal_signal, 1 );
+ init_one_signal (SIGSEGV, got_fatal_signal, 1 );
+ init_one_signal (SIGUSR1, got_usr_signal, 0 );
+ init_one_signal (SIGPIPE, SIG_IGN, 0 );
+#endif
+}
+
+void
+gnupg_pause_on_sigusr (int which)
+{
+#ifndef HAVE_DOSISH_SYSTEM
+# ifdef HAVE_SIGPROCMASK
+ sigset_t mask, oldmask;
+
+ assert (which == 1);
+ sigemptyset( &mask );
+ sigaddset( &mask, SIGUSR1 );
+
+ sigprocmask( SIG_BLOCK, &mask, &oldmask );
+ while (!caught_sigusr1)
+ sigsuspend (&oldmask);
+ caught_sigusr1 = 0;
+ sigprocmask (SIG_UNBLOCK, &mask, NULL);
+# else
+ assert (which == 1);
+ sighold (SIGUSR1);
+ while (!caught_sigusr1)
+ sigpause(SIGUSR1);
+ caught_sigusr1 = 0;
+ sigrelease(SIGUSR1);
+# endif /*!HAVE_SIGPROCMASK*/
+#endif
+}
+
+
+static void
+do_block( int block )
+{
+#ifndef HAVE_DOSISH_SYSTEM
+ static int is_blocked;
+#ifdef HAVE_SIGPROCMASK
+ static sigset_t oldmask;
+
+ if (block)
+ {
+ sigset_t newmask;
+
+ if (is_blocked)
+ log_bug ("signals are already blocked\n");
+ sigfillset( &newmask );
+ sigprocmask( SIG_BLOCK, &newmask, &oldmask );
+ is_blocked = 1;
+ }
+ else
+ {
+ if (!is_blocked)
+ log_bug("signals are not blocked\n");
+ sigprocmask (SIG_SETMASK, &oldmask, NULL);
+ is_blocked = 0;
+ }
+#else /*!HAVE_SIGPROCMASK*/
+ static void (*disposition[MAXSIG])();
+ int sig;
+
+ if (block)
+ {
+ if (is_blocked)
+ log_bug("signals are already blocked\n");
+ for (sig=1; sig < MAXSIG; sig++)
+ {
+ disposition[sig] = sigset (sig, SIG_HOLD);
+ }
+ is_blocked = 1;
+ }
+ else
+ {
+ if (!is_blocked)
+ log_bug ("signals are not blocked\n");
+ for (sig=1; sig < MAXSIG; sig++) {
+ sigset (sig, disposition[sig]);
+ }
+ is_blocked = 0;
+ }
+#endif /*!HAVE_SIGPROCMASK*/
+#endif /*HAVE_DOSISH_SYSTEM*/
+}
+
+
+void
+gnupg_block_all_signals ()
+{
+ do_block(1);
+}
+
+void
+gnupg_unblock_all_signals ()
+{
+ do_block(0);
+}
diff --git a/common/simple-gettext.c b/common/simple-gettext.c
new file mode 100644
index 000000000..56a305fd8
--- /dev/null
+++ b/common/simple-gettext.c
@@ -0,0 +1,438 @@
+/* simple-gettext.c - a simplified version of gettext.
+ * Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* This is a simplified version of gettext written by Ulrich Drepper.
+ * It is used for the Win32 version of GnuPG beucase all the overhead
+ * of gettext is not needed and we have to do some special Win32 stuff.
+ * I decided that this is far easier than to tweak gettext for the special
+ * cases (I tried it but it is a lot of code). wk 15.09.99
+ */
+
+#include <config.h>
+#ifdef USE_SIMPLE_GETTEXT
+#if !defined (_WIN32) && !defined (__CYGWIN32__)
+#error This file can only be used under Windows or Cygwin32
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "util.h"
+#include "sysutils.h"
+
+/* The magic number of the GNU message catalog format. */
+#define MAGIC 0x950412de
+#define MAGIC_SWAPPED 0xde120495
+
+/* Revision number of the currently used .mo (binary) file format. */
+#define MO_REVISION_NUMBER 0
+
+
+/* Header for binary .mo file format. */
+struct mo_file_header
+{
+ /* The magic number. */
+ u32 magic;
+ /* The revision number of the file format. */
+ u32 revision;
+ /* The number of strings pairs. */
+ u32 nstrings;
+ /* Offset of table with start offsets of original strings. */
+ u32 orig_tab_offset;
+ /* Offset of table with start offsets of translation strings. */
+ u32 trans_tab_offset;
+ /* Size of hashing table. */
+ u32 hash_tab_size;
+ /* Offset of first hashing entry. */
+ u32 hash_tab_offset;
+};
+
+struct string_desc
+{
+ /* Length of addressed string. */
+ u32 length;
+ /* Offset of string in file. */
+ u32 offset;
+};
+
+
+struct overflow_space_s
+{
+ struct overflow_space_s *next;
+ u32 idx;
+ char d[1];
+};
+
+struct loaded_domain
+{
+ char *data;
+ int must_swap;
+ u32 nstrings;
+ char *mapped; /* 0 = not yet mapped, 1 = mapped,
+ 2 = mapped to
+ overflow space */
+ struct overflow_space_s *overflow_space;
+ struct string_desc *orig_tab;
+ struct string_desc *trans_tab;
+ u32 hash_size;
+ u32 *hash_tab;
+};
+
+
+static struct loaded_domain *the_domain;
+
+static __inline__ u32
+do_swap_u32( u32 i )
+{
+ return (i << 24) | ((i & 0xff00) << 8) | ((i >> 8) & 0xff00) | (i >> 24);
+}
+
+#define SWAPIT(flag, data) ((flag) ? do_swap_u32(data) : (data) )
+
+
+/* We assume to have `unsigned long int' value with at least 32 bits. */
+#define HASHWORDBITS 32
+
+/* The so called `hashpjw' function by P.J. Weinberger
+ [see Aho/Sethi/Ullman, COMPILERS: Principles, Techniques and Tools,
+ 1986, 1987 Bell Telephone Laboratories, Inc.] */
+
+static __inline__ ulong
+hash_string( const char *str_param )
+{
+ unsigned long int hval, g;
+ const char *str = str_param;
+
+ hval = 0;
+ while (*str != '\0')
+ {
+ hval <<= 4;
+ hval += (unsigned long int) *str++;
+ g = hval & ((unsigned long int) 0xf << (HASHWORDBITS - 4));
+ if (g != 0)
+ {
+ hval ^= g >> (HASHWORDBITS - 8);
+ hval ^= g;
+ }
+ }
+ return hval;
+}
+
+
+static struct loaded_domain *
+load_domain( const char *filename )
+{
+ FILE *fp;
+ size_t size;
+ struct stat st;
+ struct mo_file_header *data = NULL;
+ struct loaded_domain *domain = NULL;
+ size_t to_read;
+ char *read_ptr;
+
+ fp = fopen( filename, "rb" );
+ if( !fp )
+ return NULL; /* can't open the file */
+ /* we must know about the size of the file */
+ if( fstat( fileno(fp ), &st )
+ || (size = (size_t)st.st_size) != st.st_size
+ || size < sizeof (struct mo_file_header) ) {
+ fclose( fp );
+ return NULL;
+ }
+
+ data = malloc( size );
+ if( !data ) {
+ fclose( fp );
+ return NULL; /* out of memory */
+ }
+
+ to_read = size;
+ read_ptr = (char *) data;
+ do {
+ long int nb = fread( read_ptr, 1, to_read, fp );
+ if( nb < to_read ) {
+ fclose (fp);
+ free(data);
+ return NULL; /* read error */
+ }
+ read_ptr += nb;
+ to_read -= nb;
+ } while( to_read > 0 );
+ fclose (fp);
+
+ /* Using the magic number we can test whether it really is a message
+ * catalog file. */
+ if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {
+ /* The magic number is wrong: not a message catalog file. */
+ free( data );
+ return NULL;
+ }
+
+ domain = calloc( 1, sizeof *domain );
+ if( !domain ) {
+ free( data );
+ return NULL;
+ }
+ domain->data = (char *) data;
+ domain->must_swap = data->magic != MAGIC;
+
+ /* Fill in the information about the available tables. */
+ switch( SWAPIT(domain->must_swap, data->revision) ) {
+ case 0:
+ domain->nstrings = SWAPIT(domain->must_swap, data->nstrings);
+ domain->orig_tab = (struct string_desc *)
+ ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));
+ domain->trans_tab = (struct string_desc *)
+ ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));
+ domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size);
+ domain->hash_tab = (u32 *)
+ ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));
+ break;
+
+ default: /* This is an invalid revision. */
+ free( data );
+ free( domain );
+ return NULL;
+ }
+
+ /* Allocate an array to keep track of code page mappings. */
+ domain->mapped = calloc( 1, domain->nstrings );
+ if( !domain->mapped ) {
+ free( data );
+ free( domain );
+ return NULL;
+ }
+
+ return domain;
+}
+
+
+/****************
+ * Set the file used for translations. Pass a NULL to disable
+ * translation. A new filename may be set at anytime.
+ * WARNING: After changing the filename you should not access any data
+ * retrieved by gettext().
+ */
+int
+set_gettext_file( const char *filename )
+{
+ struct loaded_domain *domain = NULL;
+
+ if( filename && *filename ) {
+ if( filename[0] == '/'
+#ifdef HAVE_DRIVE_LETTERS
+ || ( isalpha(filename[0])
+ && filename[1] == ':'
+ && (filename[2] == '/' || filename[2] == '\\') )
+#endif
+ ) {
+ /* absolute path - use it as is */
+ domain = load_domain( filename );
+ }
+ else { /* relative path - append ".mo" and get dir from the environment */
+ char *buf = NULL;
+ char *dir;
+ char *p;
+
+ dir = read_w32_registry_string( NULL,
+ "Control Panel\\Mingw32\\NLS",
+ "MODir" );
+ if( dir && (buf=malloc(strlen(dir)+strlen(filename)+1+3+1)) ) {
+ strcpy(stpcpy(stpcpy(stpcpy( buf, dir),"\\"), filename),".mo");
+ /* Better make sure that we don't mix forward and
+ backward slashes. It seems that some Windoze
+ versions don't accept this. */
+ for (p=buf; *p; p++)
+ {
+ if (*p == '/')
+ *p = '\\';
+ }
+ domain = load_domain( buf );
+ free(buf);
+ }
+ free(dir);
+ }
+ if( !domain )
+ return -1;
+ }
+
+ if( the_domain ) {
+ struct overflow_space_s *os, *os2;
+ free( the_domain->data );
+ free( the_domain->mapped );
+ for (os=the_domain->overflow_space; os; os = os2) {
+ os2 = os->next;
+ free (os);
+ }
+ free( the_domain );
+ the_domain = NULL;
+ }
+ the_domain = domain;
+ return 0;
+}
+
+
+static const char*
+get_string( struct loaded_domain *domain, u32 idx )
+{
+ struct overflow_space_s *os;
+ char *p;
+
+ p = domain->data + SWAPIT(domain->must_swap, domain->trans_tab[idx].offset);
+ if (!domain->mapped[idx])
+ {
+ size_t plen, buflen;
+ char *buf;
+
+ domain->mapped[idx] = 1;
+
+ plen = strlen (p);
+ buf = utf8_to_native (p, plen, -1);
+ buflen = strlen (buf);
+ if (buflen <= plen)
+ strcpy (p, buf);
+ else
+ {
+ /* There is not enough space for the translation - store it
+ in the overflow_space else and mark that in the mapped
+ array. Because we expect that this won't happen too
+ often, we use a simple linked list. */
+ os = malloc (sizeof *os + buflen);
+ if (os)
+ {
+ os->idx = idx;
+ strcpy (os->d, buf);
+ os->next = domain->overflow_space;
+ domain->overflow_space = os;
+ p = os->d;
+ }
+ else
+ p = "ERROR in GETTEXT MALLOC";
+ }
+ xfree (buf);
+ }
+ else if (domain->mapped[idx] == 2)
+ { /* We need to get the string from the overflow_space. */
+ for (os=domain->overflow_space; os; os = os->next)
+ if (os->idx == idx)
+ return (const char*)os->d;
+ p = "ERROR in GETTEXT\n";
+ }
+ return (const char*)p;
+}
+
+
+
+const char *
+gettext( const char *msgid )
+{
+ struct loaded_domain *domain;
+ size_t act = 0;
+ size_t top, bottom;
+
+ if( !(domain = the_domain) )
+ goto not_found;
+
+ /* Locate the MSGID and its translation. */
+ if( domain->hash_size > 2 && domain->hash_tab ) {
+ /* Use the hashing table. */
+ u32 len = strlen (msgid);
+ u32 hash_val = hash_string (msgid);
+ u32 idx = hash_val % domain->hash_size;
+ u32 incr = 1 + (hash_val % (domain->hash_size - 2));
+ u32 nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
+
+ if ( !nstr ) /* Hash table entry is empty. */
+ goto not_found;
+
+ if( SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].length) == len
+ && !strcmp( msgid,
+ domain->data + SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].offset)) )
+ return get_string( domain, nstr - 1 );
+
+ for(;;) {
+ if (idx >= domain->hash_size - incr)
+ idx -= domain->hash_size - incr;
+ else
+ idx += incr;
+
+ nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);
+ if( !nstr )
+ goto not_found; /* Hash table entry is empty. */
+
+ if ( SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].length) == len
+ && !strcmp (msgid,
+ domain->data + SWAPIT(domain->must_swap,
+ domain->orig_tab[nstr - 1].offset)))
+ return get_string( domain, nstr-1 );
+ }
+ /* NOTREACHED */
+ }
+
+ /* Now we try the default method: binary search in the sorted
+ array of messages. */
+ bottom = 0;
+ top = domain->nstrings;
+ while( bottom < top ) {
+ int cmp_val;
+
+ act = (bottom + top) / 2;
+ cmp_val = strcmp(msgid, domain->data
+ + SWAPIT(domain->must_swap,
+ domain->orig_tab[act].offset));
+ if (cmp_val < 0)
+ top = act;
+ else if (cmp_val > 0)
+ bottom = act + 1;
+ else
+ return get_string( domain, act );
+ }
+
+ not_found:
+ return msgid;
+}
+
+#if 0
+ unsigned int cp1, cp2;
+
+ cp1 = GetConsoleCP();
+ cp2 = GetConsoleOutputCP();
+
+ log_info("InputCP=%u OutputCP=%u\n", cp1, cp2 );
+
+ if( !SetConsoleOutputCP( 1252 ) )
+ log_info("SetConsoleOutputCP failed: %s\n", w32_strerror (0));
+
+ cp1 = GetConsoleCP();
+ cp2 = GetConsoleOutputCP();
+ log_info("InputCP=%u OutputCP=%u after switch1\n", cp1, cp2 );
+#endif
+
+#endif /* USE_SIMPLE_GETTEXT */
diff --git a/common/simple-pwquery.c b/common/simple-pwquery.c
new file mode 100644
index 000000000..e405c1ec0
--- /dev/null
+++ b/common/simple-pwquery.c
@@ -0,0 +1,630 @@
+/* simple-pwquery.c - A simple password query client for gpg-agent
+ * Copyright (C) 2002, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* This module is intended as a standalone client implementation to
+ gpg-agent's GET_PASSPHRASE command. In particular it does not use
+ the Assuan library and can only cope with an already running
+ gpg-agent. Some stuff is configurable in the header file. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#ifdef HAVE_W32_SYSTEM
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_W32_SYSTEM
+#include "../jnlib/w32-afunix.h"
+#endif
+
+
+#define SIMPLE_PWQUERY_IMPLEMENTATION 1
+#include "simple-pwquery.h"
+
+#if defined(SPWQ_USE_LOGGING) && !defined(HAVE_JNLIB_LOGGING)
+# undef SPWQ_USE_LOGGING
+#endif
+
+#ifndef _
+#define _(a) (a)
+#endif
+
+#if !defined (hexdigitp) && !defined (xtoi_2)
+#define digitp(p) (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a) \
+ || (*(a) >= 'A' && *(a) <= 'F') \
+ || (*(a) >= 'a' && *(a) <= 'f'))
+#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
+ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#endif
+
+
+
+
+
+
+#ifndef HAVE_STPCPY
+static char *
+my_stpcpy(char *a,const char *b)
+{
+ while( *b )
+ *a++ = *b++;
+ *a = 0;
+
+ return (char*)a;
+}
+#define stpcpy(a,b) my_stpcpy((a), (b))
+#endif
+
+
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+ while (nleft > 0)
+ {
+#ifdef HAVE_W32_SYSTEM
+ nwritten = send (fd, buf, nleft, 0);
+#else
+ nwritten = write (fd, buf, nleft);
+#endif
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ nwritten = 0;
+ else {
+#ifdef SPWQ_USE_LOGGING
+ log_error ("write failed: %s\n", strerror (errno));
+#endif
+ return SPWQ_IO_ERROR;
+ }
+ }
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+
+ return 0;
+}
+
+
+/* Read an entire line and return number of bytes read. */
+static int
+readline (int fd, char *buf, size_t buflen)
+{
+ size_t nleft = buflen;
+ char *p;
+ int nread = 0;
+
+ while (nleft > 0)
+ {
+#ifdef HAVE_W32_SYSTEM
+ int n = recv (fd, buf, nleft, 0);
+#else
+ int n = read (fd, buf, nleft);
+#endif
+ if (n < 0)
+ {
+ if (errno == EINTR)
+ continue;
+ return -(SPWQ_IO_ERROR);
+ }
+ else if (!n)
+ {
+ return -(SPWQ_PROTOCOL_ERROR); /* incomplete line */
+ }
+ p = buf;
+ nleft -= n;
+ buf += n;
+ nread += n;
+
+ for (; n && *p != '\n'; n--, p++)
+ ;
+ if (n)
+ {
+ break; /* at least one full line available - that's enough.
+ This function is just a simple implementation, so
+ it is okay to forget about pending bytes */
+ }
+ }
+
+ return nread;
+}
+
+
+/* Send an option to the agent */
+static int
+agent_send_option (int fd, const char *name, const char *value)
+{
+ char buf[200];
+ int nread;
+ char *line;
+ int i;
+
+ line = spwq_malloc (7 + strlen (name) + 1 + strlen (value) + 2);
+ if (!line)
+ return SPWQ_OUT_OF_CORE;
+ strcpy (stpcpy (stpcpy (stpcpy (
+ stpcpy (line, "OPTION "), name), "="), value), "\n");
+ i = writen (fd, line, strlen (line));
+ spwq_free (line);
+ if (i)
+ return i;
+
+ /* get response */
+ nread = readline (fd, buf, DIM(buf)-1);
+ if (nread < 0)
+ return -nread;
+ if (nread < 3)
+ return SPWQ_PROTOCOL_ERROR;
+
+ if (buf[0] == 'O' && buf[1] == 'K' && (buf[2] == ' ' || buf[2] == '\n'))
+ return 0; /* okay */
+
+ return SPWQ_ERR_RESPONSE;
+}
+
+
+/* Send all available options to the agent. */
+static int
+agent_send_all_options (int fd)
+{
+ char *dft_display = NULL;
+ char *dft_ttyname = NULL;
+ char *dft_ttytype = NULL;
+ int rc = 0;
+
+ dft_display = getenv ("DISPLAY");
+ if (dft_display)
+ {
+ if ((rc = agent_send_option (fd, "display", dft_display)))
+ return rc;
+ }
+
+ dft_ttyname = getenv ("GPG_TTY");
+#ifndef HAVE_W32_SYSTEM
+ if ((!dft_ttyname || !*dft_ttyname) && ttyname (0))
+ dft_ttyname = ttyname (0);
+#endif
+ if (dft_ttyname && *dft_ttyname)
+ {
+ if ((rc=agent_send_option (fd, "ttyname", dft_ttyname)))
+ return rc;
+ }
+
+ dft_ttytype = getenv ("TERM");
+ if (dft_ttyname && dft_ttytype)
+ {
+ if ((rc = agent_send_option (fd, "ttytype", dft_ttytype)))
+ return rc;
+ }
+
+#if defined(HAVE_SETLOCALE)
+ {
+ char *old_lc = NULL;
+ char *dft_lc = NULL;
+
+#if defined(LC_CTYPE)
+ old_lc = setlocale (LC_CTYPE, NULL);
+ if (old_lc)
+ {
+ char *p = spwq_malloc (strlen (old_lc)+1);
+ if (!p)
+ return SPWQ_OUT_OF_CORE;
+ strcpy (p, old_lc);
+ old_lc = p;
+ }
+ dft_lc = setlocale (LC_CTYPE, "");
+ if (dft_ttyname && dft_lc)
+ rc = agent_send_option (fd, "lc-ctype", dft_lc);
+ if (old_lc)
+ {
+ setlocale (LC_CTYPE, old_lc);
+ spwq_free (old_lc);
+ }
+ if (rc)
+ return rc;
+#endif
+
+#if defined(LC_MESSAGES)
+ old_lc = setlocale (LC_MESSAGES, NULL);
+ if (old_lc)
+ {
+ char *p = spwq_malloc (strlen (old_lc)+1);
+ if (!p)
+ return SPWQ_OUT_OF_CORE;
+ strcpy (p, old_lc);
+ old_lc = p;
+ }
+ dft_lc = setlocale (LC_MESSAGES, "");
+ if (dft_ttyname && dft_lc)
+ rc = agent_send_option (fd, "lc-messages", dft_lc);
+ if (old_lc)
+ {
+ setlocale (LC_MESSAGES, old_lc);
+ spwq_free (old_lc);
+ }
+ if (rc)
+ return rc;
+#endif
+ }
+#endif /*HAVE_SETLOCALE*/
+
+ return 0;
+}
+
+
+
+/* Try to open a connection to the agent, send all options and return
+ the file descriptor for the connection. Return -1 in case of
+ error. */
+static int
+agent_open (int *rfd)
+{
+ int rc;
+ int fd;
+ char *infostr, *p;
+ struct sockaddr_un client_addr;
+ size_t len;
+ int prot;
+ char line[200];
+ int nread;
+
+ *rfd = -1;
+ infostr = getenv ( "GPG_AGENT_INFO" );
+ if ( !infostr || !*infostr )
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error (_("gpg-agent is not available in this session\n"));
+#endif
+ return SPWQ_NO_AGENT;
+ }
+ p = spwq_malloc (strlen (infostr)+1);
+ if (!p)
+ return SPWQ_OUT_OF_CORE;
+ strcpy (p, infostr);
+ infostr = p;
+
+ if ( !(p = strchr ( infostr, PATHSEP_C)) || p == infostr
+ || (p-infostr)+1 >= sizeof client_addr.sun_path )
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
+#endif
+ return SPWQ_NO_AGENT;
+ }
+ *p++ = 0;
+
+ while (*p && *p != PATHSEP_C)
+ p++;
+ prot = *p? atoi (p+1) : 0;
+ if ( prot != 1)
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error (_("gpg-agent protocol version %d is not supported\n"),prot);
+#endif
+ return SPWQ_PROTOCOL_ERROR;
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ fd = _w32_sock_new (AF_UNIX, SOCK_STREAM, 0);
+#else
+ fd = socket (AF_UNIX, SOCK_STREAM, 0);
+#endif
+ if (fd == -1)
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error ("can't create socket: %s\n", strerror(errno) );
+#endif
+ return SPWQ_SYS_ERROR;
+ }
+
+ memset (&client_addr, 0, sizeof client_addr);
+ client_addr.sun_family = AF_UNIX;
+ strcpy (client_addr.sun_path, infostr);
+ len = (offsetof (struct sockaddr_un, sun_path)
+ + strlen(client_addr.sun_path) + 1);
+
+#ifdef HAVE_W32_SYSTEM
+ rc = _w32_sock_connect (fd, (struct sockaddr*)&client_addr, len );
+#else
+ rc = connect (fd, (struct sockaddr*)&client_addr, len );
+#endif
+ if (rc == -1)
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error ( _("can't connect to `%s': %s\n"), infostr, strerror (errno));
+#endif
+ close (fd );
+ return SPWQ_IO_ERROR;
+ }
+
+ nread = readline (fd, line, DIM(line));
+ if (nread < 3 || !(line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\n' || line[2] == ' ')) )
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error ( _("communication problem with gpg-agent\n"));
+#endif
+ close (fd );
+ return SPWQ_PROTOCOL_ERROR;
+ }
+
+ rc = agent_send_all_options (fd);
+ if (rc)
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error (_("problem setting the gpg-agent options\n"));
+#endif
+ close (fd);
+ return rc;
+ }
+
+ *rfd = fd;
+ return 0;
+}
+
+
+/* Copy text to BUFFER and escape as required. Return a pointer to
+ the end of the new buffer. Note that BUFFER must be large enough
+ to keep the entire text; allocataing it 3 times the size of TEXT
+ is sufficient. */
+static char *
+copy_and_escape (char *buffer, const char *text)
+{
+ int i;
+ const unsigned char *s = (unsigned char *)text;
+ char *p = buffer;
+
+
+ for (i=0; s[i]; i++)
+ {
+ if (s[i] < ' ' || s[i] == '+')
+ {
+ sprintf (p, "%%%02X", s[i]);
+ p += 3;
+ }
+ else if (s[i] == ' ')
+ *p++ = '+';
+ else
+ *p++ = s[i];
+ }
+ return p;
+}
+
+
+/* Ask the gpg-agent for a passphrase and present the user with a
+ DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
+ If a CACHEID is not NULL it is used to locate the passphrase in in
+ the cache and store it under this ID. If ERRORCODE is not NULL it
+ should point a variable receiving an errorcode; thsi errocode might
+ be 0 if the user canceled the operation. The function returns NULL
+ to indicate an error. */
+char *
+simple_pwquery (const char *cacheid,
+ const char *tryagain,
+ const char *prompt,
+ const char *description,
+ int *errorcode)
+{
+ int fd = -1;
+ int nread;
+ char *result = NULL;
+ char *pw = NULL;
+ char *p;
+ int rc, i;
+
+ rc = agent_open (&fd);
+ if (rc)
+ goto leave;
+
+ if (!cacheid)
+ cacheid = "X";
+ if (!tryagain)
+ tryagain = "X";
+ if (!prompt)
+ prompt = "X";
+ if (!description)
+ description = "X";
+
+ {
+ char *line;
+ /* We allocate 3 times the needed space so that there is enough
+ space for escaping. */
+ line = spwq_malloc (15
+ + 3*strlen (cacheid) + 1
+ + 3*strlen (tryagain) + 1
+ + 3*strlen (prompt) + 1
+ + 3*strlen (description) + 1
+ + 2);
+ if (!line)
+ {
+ rc = SPWQ_OUT_OF_CORE;
+ goto leave;
+ }
+ strcpy (line, "GET_PASSPHRASE ");
+ p = line+15;
+ p = copy_and_escape (p, cacheid);
+ *p++ = ' ';
+ p = copy_and_escape (p, tryagain);
+ *p++ = ' ';
+ p = copy_and_escape (p, prompt);
+ *p++ = ' ';
+ p = copy_and_escape (p, description);
+ *p++ = '\n';
+ rc = writen (fd, line, p - line);
+ spwq_free (line);
+ if (rc)
+ goto leave;
+ }
+
+ /* get response */
+ pw = spwq_secure_malloc (500);
+ nread = readline (fd, pw, 499);
+ if (nread < 0)
+ {
+ rc = -nread;
+ goto leave;
+ }
+ if (nread < 3)
+ {
+ rc = SPWQ_PROTOCOL_ERROR;
+ goto leave;
+ }
+
+ if (pw[0] == 'O' && pw[1] == 'K' && pw[2] == ' ')
+ { /* we got a passphrase - convert it back from hex */
+ size_t pwlen = 0;
+
+ for (i=3; i < nread && hexdigitp (pw+i); i+=2)
+ pw[pwlen++] = xtoi_2 (pw+i);
+ pw[pwlen] = 0; /* make a C String */
+ result = pw;
+ pw = NULL;
+ }
+ else if ((nread > 7 && !memcmp (pw, "ERR 111", 7)
+ && (pw[7] == ' ' || pw[7] == '\n') )
+ || ((nread > 4 && !memcmp (pw, "ERR ", 4)
+ && (strtoul (pw+4, NULL, 0) & 0xffff) == 99)) )
+ {
+ /* 111 is the old Assuan code for canceled which might still
+ be in use by old installations. 99 is GPG_ERR_CANCELED as
+ used by modern gpg-agents; 0xffff is used to mask out the
+ error source. */
+#ifdef SPWQ_USE_LOGGING
+ log_info (_("canceled by user\n") );
+#endif
+ *errorcode = 0; /* canceled */
+ }
+ else
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error (_("problem with the agent\n"));
+#endif
+ rc = SPWQ_ERR_RESPONSE;
+ }
+
+ leave:
+ if (errorcode)
+ *errorcode = rc;
+ if (fd != -1)
+ close (fd);
+ if (pw)
+ spwq_secure_free (pw);
+ return result;
+}
+
+
+/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */
+int
+simple_pwclear (const char *cacheid)
+{
+ char line[500];
+ char *p;
+
+ /* We need not more than 50 characters for the command and the
+ terminating nul. */
+ if (strlen (cacheid) * 3 > sizeof (line) - 50)
+ return SPWQ_PROTOCOL_ERROR;
+
+ strcpy (line, "CLEAR_PASSPHRASE ");
+ p = line + 17;
+ p = copy_and_escape (p, cacheid);
+ *p++ = '\n';
+ *p++ = '\0';
+
+ return simple_query (line);
+}
+
+
+/* Perform the simple query QUERY (which must be new-line and 0
+ terminated) and return the error code. */
+int
+simple_query (const char *query)
+{
+ int fd = -1;
+ int nread;
+ char response[500];
+ int rc;
+
+ rc = agent_open (&fd);
+ if (rc)
+ goto leave;
+
+ rc = writen (fd, query, strlen (query));
+ if (rc)
+ goto leave;
+
+ /* get response */
+ nread = readline (fd, response, 499);
+ if (nread < 0)
+ {
+ rc = -nread;
+ goto leave;
+ }
+ if (nread < 3)
+ {
+ rc = SPWQ_PROTOCOL_ERROR;
+ goto leave;
+ }
+
+ if (response[0] == 'O' && response[1] == 'K')
+ /* OK, do nothing. */;
+ else if ((nread > 7 && !memcmp (response, "ERR 111", 7)
+ && (response[7] == ' ' || response[7] == '\n') )
+ || ((nread > 4 && !memcmp (response, "ERR ", 4)
+ && (strtoul (response+4, NULL, 0) & 0xffff) == 99)) )
+ {
+ /* 111 is the old Assuan code for canceled which might still
+ be in use by old installations. 99 is GPG_ERR_CANCELED as
+ used by modern gpg-agents; 0xffff is used to mask out the
+ error source. */
+#ifdef SPWQ_USE_LOGGING
+ log_info (_("canceled by user\n") );
+#endif
+ }
+ else
+ {
+#ifdef SPWQ_USE_LOGGING
+ log_error (_("problem with the agent\n"));
+#endif
+ rc = SPWQ_ERR_RESPONSE;
+ }
+
+ leave:
+ if (fd != -1)
+ close (fd);
+ return rc;
+}
diff --git a/common/simple-pwquery.h b/common/simple-pwquery.h
new file mode 100644
index 000000000..5b941d06f
--- /dev/null
+++ b/common/simple-pwquery.h
@@ -0,0 +1,76 @@
+/* simple-pwquery.c - A simple password query cleint for gpg-agent
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef SIMPLE_PWQUERY_H
+#define SIMPLE_PWQUERY_H
+
+#ifdef SIMPLE_PWQUERY_IMPLEMENTATION /* Begin configuration stuff. */
+
+/* Include whatever files you need. */
+#include <gcrypt.h>
+#include "../jnlib/logging.h"
+
+/* Try to write error message using the standard log mechanism. The
+ current implementation requires that the HAVE_JNLIB_LOGGING is also
+ defined. */
+#define SPWQ_USE_LOGGING 1
+
+/* Memory allocation functions used by the implementation. Note, that
+ the returned value is expected to be freed with
+ spwq_secure_free. */
+#define spwq_malloc(a) gcry_malloc (a)
+#define spwq_free(a) gcry_free (a)
+#define spwq_secure_malloc(a) gcry_malloc_secure (a)
+#define spwq_secure_free(a) gcry_free (a)
+
+
+#endif /*SIMPLE_PWQUERY_IMPLEMENTATION*/ /* End configuration stuff. */
+
+
+/* Ask the gpg-agent for a passphrase and present the user with a
+ DESCRIPTION, a PROMPT and optiaonlly with a TRYAGAIN extra text.
+ If a CACHEID is not NULL it is used to locate the passphrase in in
+ the cache and store it under this ID. If ERRORCODE is not NULL it
+ should point a variable receiving an errorcode; this errocode might
+ be 0 if the user canceled the operation. The function returns NULL
+ to indicate an error. */
+char *simple_pwquery (const char *cacheid,
+ const char *tryagain,
+ const char *prompt,
+ const char *description,
+ int *errorcode);
+
+/* Ask the gpg-agent to clear the passphrase for the cache ID CACHEID. */
+int simple_pwclear (const char *cacheid);
+
+/* Perform the simple query QUERY (which must be new-line and 0
+ terminated) and return the error code. */
+int simple_query (const char *query);
+
+#define SPWQ_OUT_OF_CORE 1
+#define SPWQ_IO_ERROR 2
+#define SPWQ_PROTOCOL_ERROR 3
+#define SPWQ_ERR_RESPONSE 4
+#define SPWQ_NO_AGENT 5
+#define SPWQ_SYS_ERROR 6
+#define SPWQ_GENERAL_ERROR 7
+
+#endif /*SIMPLE_PWQUERY_H*/
diff --git a/common/sysutils.c b/common/sysutils.c
new file mode 100644
index 000000000..3e52cdaa3
--- /dev/null
+++ b/common/sysutils.c
@@ -0,0 +1,231 @@
+/* sysutils.c - system helpers
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#ifdef HAVE_STAT
+#include <sys/stat.h>
+#endif
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+ #include <asm/sysinfo.h>
+ #include <asm/unistd.h>
+#endif
+#ifdef HAVE_SETRLIMIT
+ #include <time.h>
+ #include <sys/time.h>
+ #include <sys/resource.h>
+#endif
+#include "util.h"
+#include "i18n.h"
+
+#include "sysutils.h"
+
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+#warning using trap_unaligned
+static int
+setsysinfo(unsigned long op, void *buffer, unsigned long size,
+ int *start, void *arg, unsigned long flag)
+{
+ return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag);
+}
+
+void
+trap_unaligned(void)
+{
+ unsigned int buf[2];
+
+ buf[0] = SSIN_UACPROC;
+ buf[1] = UAC_SIGBUS | UAC_NOPRINT;
+ setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0);
+}
+#else
+void
+trap_unaligned(void)
+{ /* dummy */
+}
+#endif
+
+
+int
+disable_core_dumps (void)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ return 0;
+#else
+# ifdef HAVE_SETRLIMIT
+ struct rlimit limit;
+
+ /* We only set the current limit unless we were not able to
+ retrieve the old value. */
+ if (getrlimit (RLIMIT_CORE, &limit))
+ limit.rlim_max = 0;
+ limit.rlim_cur = 0;
+ if( !setrlimit (RLIMIT_CORE, &limit) )
+ return 0;
+ if( errno != EINVAL && errno != ENOSYS )
+ log_fatal (_("can't disable core dumps: %s\n"), strerror(errno) );
+#endif
+ return 1;
+#endif
+}
+
+int
+enable_core_dumps (void)
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ return 0;
+#else
+# ifdef HAVE_SETRLIMIT
+ struct rlimit limit;
+
+ if (getrlimit (RLIMIT_CORE, &limit))
+ return 1;
+ limit.rlim_cur = limit.rlim_max;
+ setrlimit (RLIMIT_CORE, &limit);
+ return 1; /* We always return true because trhis function is
+ merely a debugging aid. */
+# endif
+ return 1;
+#endif
+}
+
+
+
+/* Return a string which is used as a kind of process ID */
+const byte *
+get_session_marker( size_t *rlen )
+{
+ static byte marker[SIZEOF_UNSIGNED_LONG*2];
+ static int initialized;
+
+ if ( !initialized ) {
+ volatile ulong aa, bb; /* we really want the uninitialized value */
+ ulong a, b;
+
+ initialized = 1;
+ /* also this marker is guessable it is not easy to use this
+ * for a faked control packet because an attacker does not
+ * have enough control about the time the verification does
+ * take place. Of course, we can add just more random but
+ * than we need the random generator even for verification
+ * tasks - which does not make sense. */
+ a = aa ^ (ulong)getpid();
+ b = bb ^ (ulong)time(NULL);
+ memcpy( marker, &a, SIZEOF_UNSIGNED_LONG );
+ memcpy( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG );
+ }
+ *rlen = sizeof(marker);
+ return marker;
+}
+
+
+#if 0 /* not yet needed - Note that this will require inclusion of
+ cmacros.am in Makefile.am */
+int
+check_permissions(const char *path,int extension,int checkonly)
+{
+#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
+ char *tmppath;
+ struct stat statbuf;
+ int ret=1;
+ int isdir=0;
+
+ if(opt.no_perm_warn)
+ return 0;
+
+ if(extension && path[0]!=DIRSEP_C)
+ {
+ if(strchr(path,DIRSEP_C))
+ tmppath=make_filename(path,NULL);
+ else
+ tmppath=make_filename(GNUPG_LIBDIR,path,NULL);
+ }
+ else
+ tmppath=m_strdup(path);
+
+ /* It's okay if the file doesn't exist */
+ if(stat(tmppath,&statbuf)!=0)
+ {
+ ret=0;
+ goto end;
+ }
+
+ isdir=S_ISDIR(statbuf.st_mode);
+
+ /* Per-user files must be owned by the user. Extensions must be
+ owned by the user or root. */
+ if((!extension && statbuf.st_uid != getuid()) ||
+ (extension && statbuf.st_uid!=0 && statbuf.st_uid!=getuid()))
+ {
+ if(!checkonly)
+ log_info(_("Warning: unsafe ownership on %s \"%s\"\n"),
+ isdir?"directory":extension?"extension":"file",path);
+ goto end;
+ }
+
+ /* This works for both directories and files - basically, we don't
+ care what the owner permissions are, so long as the group and
+ other permissions are 0 for per-user files, and non-writable for
+ extensions. */
+ if((extension && (statbuf.st_mode & (S_IWGRP|S_IWOTH)) !=0) ||
+ (!extension && (statbuf.st_mode & (S_IRWXG|S_IRWXO)) != 0))
+ {
+ char *dir;
+
+ /* However, if the directory the directory/file is in is owned
+ by the user and is 700, then this is not a problem.
+ Theoretically, we could walk this test up to the root
+ directory /, but for the sake of sanity, I'm stopping at one
+ level down. */
+
+ dir= make_dirname (tmppath);
+ if(stat(dir,&statbuf)==0 && statbuf.st_uid==getuid() &&
+ S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+ {
+ xfree (dir);
+ ret=0;
+ goto end;
+ }
+
+ m_free(dir);
+
+ if(!checkonly)
+ log_info(_("Warning: unsafe permissions on %s \"%s\"\n"),
+ isdir?"directory":extension?"extension":"file",path);
+ goto end;
+ }
+
+ ret=0;
+
+ end:
+ m_free(tmppath);
+
+ return ret;
+
+#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
+
+ return 0;
+}
+#endif
diff --git a/common/sysutils.h b/common/sysutils.h
new file mode 100644
index 000000000..c40dbfaa9
--- /dev/null
+++ b/common/sysutils.h
@@ -0,0 +1,47 @@
+/* sysutils.h - System utility functions for Gnupg
+ * Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_SYSUTILS_H
+#define GNUPG_COMMON_SYSUTILS_H
+
+void trap_unaligned (void);
+int disable_core_dumps (void);
+int enable_core_dumps (void);
+const unsigned char *get_session_marker (size_t *rlen);
+int check_permissions (const char *path,int extension,int checkonly);
+
+#ifdef HAVE_W32_SYSTEM
+/* Windows declares sleep as obsolete, but provides a definition for
+ _sleep but non for the still existing sleep. */
+#define sleep(a) _sleep ((a))
+
+/*-- w32reg.c --*/
+char *read_w32_registry_string( const char *root,
+ const char *dir, const char *name );
+int write_w32_registry_string(const char *root, const char *dir,
+ const char *name, const char *value);
+
+#endif /*HAVE_W32_SYSTEM*/
+
+
+
+
+#endif /*GNUPG_COMMON_SYSUTILS_H*/
diff --git a/common/ttyio.c b/common/ttyio.c
new file mode 100644
index 000000000..38883afa5
--- /dev/null
+++ b/common/ttyio.c
@@ -0,0 +1,599 @@
+/* ttyio.c - tty i/O functions
+ * Copyright (C) 1998,1999,2000,2001,2002,2003,
+ * 2004, 2006 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#ifdef HAVE_TCGETATTR
+#include <termios.h>
+#else
+#ifdef HAVE_TERMIO_H
+/* simulate termios with termio */
+#include <termio.h>
+#define termios termio
+#define tcsetattr ioctl
+#define TCSAFLUSH TCSETAF
+#define tcgetattr(A,B) ioctl(A,TCGETA,B)
+#define HAVE_TCGETATTR
+#endif
+#endif
+#ifdef _WIN32 /* use the odd Win32 functions */
+#include <windows.h>
+#ifdef HAVE_TCGETATTR
+#error mingw32 and termios
+#endif
+#endif
+#include <errno.h>
+#include <ctype.h>
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+
+#include "util.h"
+#include "memory.h"
+#include "ttyio.h"
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+#ifdef _WIN32 /* use the odd Win32 functions */
+static struct {
+ HANDLE in, out;
+} con;
+#define DEF_INPMODE (ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT \
+ |ENABLE_PROCESSED_INPUT )
+#define HID_INPMODE (ENABLE_LINE_INPUT|ENABLE_PROCESSED_INPUT )
+#define DEF_OUTMODE (ENABLE_WRAP_AT_EOL_OUTPUT|ENABLE_PROCESSED_OUTPUT)
+
+#else /* yeah, we have a real OS */
+static FILE *ttyfp = NULL;
+#endif
+
+static int initialized;
+static int last_prompt_len;
+static int batchmode;
+static int no_terminal;
+
+#ifdef HAVE_TCGETATTR
+ static struct termios termsave;
+ static int restore_termios;
+#endif
+
+
+
+/* This is a wrapper around ttyname so that we can use it even when
+ the standard streams are redirected. It figures the name out the
+ first time and returns it in a statically allocated buffer. */
+const char *
+tty_get_ttyname (void)
+{
+ static char *name;
+
+ /* On a GNU system ctermid() always return /dev/tty, so this does
+ not make much sense - however if it is ever changed we do the
+ Right Thing now. */
+#ifdef HAVE_CTERMID
+ static int got_name;
+
+ if (!got_name)
+ {
+ const char *s;
+ /* Note that despite our checks for these macros the function is
+ not necessarily thread save. We mainly do this for
+ portability reasons, in case L_ctermid is not defined. */
+# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) || defined(_POSIX_TRHEADS)
+ char buffer[L_ctermid];
+ s = ctermid (buffer);
+# else
+ s = ctermid (NULL);
+# endif
+ if (s)
+ name = strdup (s);
+ got_name = 1;
+ }
+#endif /*HAVE_CTERMID*/
+ /* Assume the standard tty on memory error or when tehre is no
+ certmid. */
+ return name? name : "/dev/tty";
+}
+
+
+
+#ifdef HAVE_TCGETATTR
+static void
+cleanup(void)
+{
+ if( restore_termios ) {
+ restore_termios = 0; /* do it prios in case it is interrupted again */
+ if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
+ log_error("tcsetattr() failed: %s\n", strerror(errno) );
+ }
+}
+#endif
+
+static void
+init_ttyfp(void)
+{
+ if( initialized )
+ return;
+
+#if defined(_WIN32)
+ {
+ SECURITY_ATTRIBUTES sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ con.out = CreateFileA( "CONOUT$", GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, 0 );
+ if( con.out == INVALID_HANDLE_VALUE )
+ log_fatal("open(CONOUT$) failed: rc=%d", (int)GetLastError() );
+ memset(&sa, 0, sizeof(sa));
+ sa.nLength = sizeof(sa);
+ sa.bInheritHandle = TRUE;
+ con.in = CreateFileA( "CONIN$", GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ &sa, OPEN_EXISTING, 0, 0 );
+ if( con.in == INVALID_HANDLE_VALUE )
+ log_fatal("open(CONIN$) failed: rc=%d", (int)GetLastError() );
+ }
+ SetConsoleMode(con.in, DEF_INPMODE );
+ SetConsoleMode(con.out, DEF_OUTMODE );
+
+#elif defined(__EMX__)
+ ttyfp = stdout; /* Fixme: replace by the real functions: see wklib */
+#else
+ ttyfp = batchmode? stderr : fopen (tty_get_ttyname (), "r+");
+ if( !ttyfp ) {
+ log_error("cannot open `%s': %s\n", tty_get_ttyname (),
+ strerror(errno) );
+ exit(2);
+ }
+#endif
+#ifdef HAVE_TCGETATTR
+ atexit( cleanup );
+#endif
+ initialized = 1;
+}
+
+
+#ifdef HAVE_LIBREADLINE
+void
+tty_enable_completion(rl_completion_func_t *completer)
+{
+/* if( no_terminal ) */
+/* return; */
+
+/* if( !initialized ) */
+/* init_ttyfp(); */
+
+/* rl_attempted_completion_function=completer; */
+/* rl_inhibit_completion=0; */
+}
+
+void
+tty_disable_completion(void)
+{
+/* if( no_terminal ) */
+/* return; */
+
+/* if( !initialized ) */
+/* init_ttyfp(); */
+
+/* rl_inhibit_completion=1; */
+}
+#endif /*HAVE_LIBREADLINE*/
+
+
+int
+tty_batchmode( int onoff )
+{
+ int old = batchmode;
+ if( onoff != -1 )
+ batchmode = onoff;
+ return old;
+}
+
+int
+tty_no_terminal(int onoff)
+{
+ int old = no_terminal;
+ no_terminal = onoff ? 1 : 0;
+ return old;
+}
+
+void
+tty_printf( const char *fmt, ... )
+{
+ va_list arg_ptr;
+
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ va_start( arg_ptr, fmt ) ;
+#ifdef _WIN32
+ {
+ char *buf = NULL;
+ int n;
+ DWORD nwritten;
+
+ n = vasprintf(&buf, fmt, arg_ptr);
+ if( !buf )
+ log_bug("vasprintf() failed\n");
+
+ if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
+ log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
+ if( n != nwritten )
+ log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten );
+ last_prompt_len += n;
+ xfree (buf);
+ }
+#else
+ last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
+ fflush(ttyfp);
+#endif
+ va_end(arg_ptr);
+}
+
+
+/* Same as tty_printf but if FP is not NULL, behave like a regualr
+ fprintf. */
+void
+tty_fprintf (FILE *fp, const char *fmt, ... )
+{
+ va_list arg_ptr;
+
+ if (fp)
+ {
+ va_start (arg_ptr, fmt) ;
+ vfprintf (fp, fmt, arg_ptr );
+ va_end (arg_ptr);
+ return;
+ }
+
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ va_start( arg_ptr, fmt ) ;
+#ifdef _WIN32
+ {
+ char *buf = NULL;
+ int n;
+ DWORD nwritten;
+
+ n = vasprintf(&buf, fmt, arg_ptr);
+ if( !buf )
+ log_bug("vasprintf() failed\n");
+
+ if( !WriteConsoleA( con.out, buf, n, &nwritten, NULL ) )
+ log_fatal("WriteConsole failed: rc=%d", (int)GetLastError() );
+ if( n != nwritten )
+ log_fatal("WriteConsole failed: %d != %d\n", n, (int)nwritten );
+ last_prompt_len += n;
+ xfree (buf);
+ }
+#else
+ last_prompt_len += vfprintf(ttyfp,fmt,arg_ptr) ;
+ fflush(ttyfp);
+#endif
+ va_end(arg_ptr);
+}
+
+
+/****************
+ * Print a string, but filter all control characters out.
+ */
+void
+tty_print_string ( const byte *p, size_t n )
+{
+ if (no_terminal)
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+#ifdef _WIN32
+ /* not so effective, change it if you want */
+ for( ; n; n--, p++ )
+ if( iscntrl( *p ) ) {
+ if( *p == '\n' )
+ tty_printf("\\n");
+ else if( !*p )
+ tty_printf("\\0");
+ else
+ tty_printf("\\x%02x", *p);
+ }
+ else
+ tty_printf("%c", *p);
+#else
+ for( ; n; n--, p++ )
+ if( iscntrl( *p ) ) {
+ putc('\\', ttyfp);
+ if( *p == '\n' )
+ putc('n', ttyfp);
+ else if( !*p )
+ putc('0', ttyfp);
+ else
+ fprintf(ttyfp, "x%02x", *p );
+ }
+ else
+ putc(*p, ttyfp);
+#endif
+}
+
+void
+tty_print_utf8_string2( const byte *p, size_t n, size_t max_n )
+{
+ size_t i;
+ char *buf;
+
+ if (no_terminal)
+ return;
+
+ /* we can handle plain ascii simpler, so check for it first */
+ for(i=0; i < n; i++ ) {
+ if( p[i] & 0x80 )
+ break;
+ }
+ if( i < n ) {
+ buf = utf8_to_native( (const char *)p, n, 0 );
+ if( max_n && (strlen( buf ) > max_n )) {
+ buf[max_n] = 0;
+ }
+ /*(utf8 conversion already does the control character quoting)*/
+ tty_printf("%s", buf );
+ xfree( buf );
+ }
+ else {
+ if( max_n && (n > max_n) ) {
+ n = max_n;
+ }
+ tty_print_string( p, n );
+ }
+}
+
+void
+tty_print_utf8_string( const byte *p, size_t n )
+{
+ tty_print_utf8_string2( p, n, 0 );
+}
+
+
+static char *
+do_get( const char *prompt, int hidden )
+{
+ char *buf;
+#ifndef __riscos__
+ byte cbuf[1];
+#endif
+ int c, n, i;
+
+ if( batchmode ) {
+ log_error("Sorry, we are in batchmode - can't get input\n");
+ exit(2);
+ }
+
+ if (no_terminal) {
+ log_error("Sorry, no terminal at all requested - can't get input\n");
+ exit(2);
+ }
+
+ if( !initialized )
+ init_ttyfp();
+
+ last_prompt_len = 0;
+ tty_printf( "%s", prompt );
+ buf = xmalloc((n=50));
+ i = 0;
+
+#ifdef _WIN32 /* windoze version */
+ if( hidden )
+ SetConsoleMode(con.in, HID_INPMODE );
+
+ for(;;) {
+ DWORD nread;
+
+ if( !ReadConsoleA( con.in, cbuf, 1, &nread, NULL ) )
+ log_fatal("ReadConsole failed: rc=%d", (int)GetLastError() );
+ if( !nread )
+ continue;
+ if( *cbuf == '\n' )
+ break;
+
+ if( !hidden )
+ last_prompt_len++;
+ c = *cbuf;
+ if( c == '\t' )
+ c = ' ';
+ else if( c > 0xa0 )
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ else if( iscntrl(c) )
+ continue;
+ if( !(i < n-1) ) {
+ n += 50;
+ buf = xrealloc (buf, n);
+ }
+ buf[i++] = c;
+ }
+
+ if( hidden )
+ SetConsoleMode(con.in, DEF_INPMODE );
+
+#elif defined(__riscos__)
+ do {
+ c = riscos_getchar();
+ if (c == 0xa || c == 0xd) { /* Return || Enter */
+ c = (int) '\n';
+ } else if (c == 0x8 || c == 0x7f) { /* Backspace || Delete */
+ if (i>0) {
+ i--;
+ if (!hidden) {
+ last_prompt_len--;
+ fputc(8, ttyfp);
+ fputc(32, ttyfp);
+ fputc(8, ttyfp);
+ fflush(ttyfp);
+ }
+ } else {
+ fputc(7, ttyfp);
+ fflush(ttyfp);
+ }
+ continue;
+ } else if (c == (int) '\t') { /* Tab */
+ c = ' ';
+ } else if (c > 0xa0) {
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ } else if (iscntrl(c)) {
+ continue;
+ }
+ if(!(i < n-1)) {
+ n += 50;
+ buf = xrealloc (buf, n);
+ }
+ buf[i++] = c;
+ if (!hidden) {
+ last_prompt_len++;
+ fputc(c, ttyfp);
+ fflush(ttyfp);
+ }
+ } while (c != '\n');
+ i = (i>0) ? i-1 : 0;
+#else /* unix version */
+ if( hidden ) {
+#ifdef HAVE_TCGETATTR
+ struct termios term;
+
+ if( tcgetattr(fileno(ttyfp), &termsave) )
+ log_fatal("tcgetattr() failed: %s\n", strerror(errno) );
+ restore_termios = 1;
+ term = termsave;
+ term.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
+ if( tcsetattr( fileno(ttyfp), TCSAFLUSH, &term ) )
+ log_fatal("tcsetattr() failed: %s\n", strerror(errno) );
+#endif
+ }
+
+ /* fixme: How can we avoid that the \n is echoed w/o disabling
+ * canonical mode - w/o this kill_prompt can't work */
+ while( read(fileno(ttyfp), cbuf, 1) == 1 && *cbuf != '\n' ) {
+ if( !hidden )
+ last_prompt_len++;
+ c = *cbuf;
+ if( c == CONTROL_D )
+ log_info("control d found\n");
+ if( c == '\t' )
+ c = ' ';
+ else if( c > 0xa0 )
+ ; /* we don't allow 0xa0, as this is a protected blank which may
+ * confuse the user */
+ else if( iscntrl(c) )
+ continue;
+ if( !(i < n-1) ) {
+ n += 50;
+ buf = xrealloc (buf, n );
+ }
+ buf[i++] = c;
+ }
+ if( *cbuf != '\n' ) {
+ buf[0] = CONTROL_D;
+ i = 1;
+ }
+
+
+ if( hidden ) {
+#ifdef HAVE_TCGETATTR
+ if( tcsetattr(fileno(ttyfp), TCSAFLUSH, &termsave) )
+ log_error("tcsetattr() failed: %s\n", strerror(errno) );
+ restore_termios = 0;
+#endif
+ }
+#endif /* end unix version */
+ buf[i] = 0;
+ return buf;
+}
+
+
+char *
+tty_get( const char *prompt )
+{
+ return do_get( prompt, 0 );
+}
+
+char *
+tty_get_hidden( const char *prompt )
+{
+ return do_get( prompt, 1 );
+}
+
+
+void
+tty_kill_prompt()
+{
+ if ( no_terminal )
+ return;
+
+ if( !initialized )
+ init_ttyfp();
+
+ if( batchmode )
+ last_prompt_len = 0;
+ if( !last_prompt_len )
+ return;
+#ifdef _WIN32
+ tty_printf("\r%*s\r", last_prompt_len, "");
+#else
+ {
+ int i;
+ putc('\r', ttyfp);
+ for(i=0; i < last_prompt_len; i ++ )
+ putc(' ', ttyfp);
+ putc('\r', ttyfp);
+ fflush(ttyfp);
+ }
+#endif
+ last_prompt_len = 0;
+}
+
+
+int
+tty_get_answer_is_yes( const char *prompt )
+{
+ int yes;
+ char *p = tty_get( prompt );
+ tty_kill_prompt();
+ yes = answer_is_yes(p);
+ xfree(p);
+ return yes;
+}
diff --git a/common/ttyio.h b/common/ttyio.h
new file mode 100644
index 000000000..32d159863
--- /dev/null
+++ b/common/ttyio.h
@@ -0,0 +1,61 @@
+/* ttyio.h
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GNUPG.
+ *
+ * GNUPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GNUPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef GNUPG_COMMON_TTYIO_H
+#define GNUPG_COMMON_TTYIO_H
+
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif
+
+const char *tty_get_ttyname (void);
+int tty_batchmode (int onoff);
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+void tty_printf (const char *fmt, ... )
+ __attribute__ ((format (printf,1,2)));
+void tty_fprintf (FILE *fp, const char *fmt, ... )
+ __attribute__ ((format (printf,2,3)));
+#else
+void tty_printf (const char *fmt, ... );
+void tty_fprintf (FILE *fp, const char *fmt, ... );
+#endif
+void tty_print_string (const unsigned char *p, size_t n);
+void tty_print_utf8_string (const unsigned char *p, size_t n);
+void tty_print_utf8_string2 (const unsigned char *p, size_t n, size_t max_n);
+char *tty_get (const char *prompt);
+char *tty_get_hidden (const char *prompt);
+void tty_kill_prompt (void);
+int tty_get_answer_is_yes (const char *prompt);
+int tty_no_terminal (int onoff);
+
+#ifdef HAVE_LIBREADLINE
+void tty_enable_completion(rl_completion_func_t *completer);
+void tty_disable_completion(void);
+#else
+/* Use a macro to stub out these functions since a macro has no need
+ to typedef a "rl_completion_func_t" which would be undefined
+ without readline. */
+#define tty_enable_completion(x)
+#define tty_disable_completion()
+#endif
+
+
+#endif /*GNUPG_COMMON_TTYIO_H*/
diff --git a/common/util.h b/common/util.h
new file mode 100644
index 000000000..29106bf9c
--- /dev/null
+++ b/common/util.h
@@ -0,0 +1,209 @@
+/* util.h - Utility functions for GnuPG
+ * Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GNUPG_COMMON_UTIL_H
+#define GNUPG_COMMON_UTIL_H
+
+#include <gcrypt.h> /* We need this for the memory function protos. */
+#include <time.h> /* We need time_t. */
+#include <gpg-error.h> /* we need gpg-error_t. */
+
+/* Common GNUlib includes (-I ../gl/). */
+#include "strpbrk.h"
+#include "strsep.h"
+#include "vasprintf.h"
+
+
+/* Hash function used with libksba. */
+#define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write)
+
+/* get all the stuff from jnlib */
+#include "../jnlib/logging.h"
+#include "../jnlib/argparse.h"
+#include "../jnlib/stringhelp.h"
+#include "../jnlib/mischelp.h"
+#include "../jnlib/strlist.h"
+#include "../jnlib/dotlock.h"
+#include "../jnlib/utf8conv.h"
+
+/* Handy malloc macros - please use only them. */
+#define xtrymalloc(a) gcry_malloc ((a))
+#define xtrymalloc_secure(a) gcry_malloc_secure ((a))
+#define xtrycalloc(a,b) gcry_calloc ((a),(b))
+#define xtrycalloc_secure(a,b) gcry_calloc_secure ((a),(b))
+#define xtryrealloc(a,b) gcry_realloc ((a),(b))
+#define xtrystrdup(a) gcry_strdup ((a))
+#define xfree(a) gcry_free ((a))
+
+#define xmalloc(a) gcry_xmalloc ((a))
+#define xmalloc_secure(a) gcry_xmalloc_secure ((a))
+#define xcalloc(a,b) gcry_xcalloc ((a),(b))
+#define xcalloc_secure(a,b) gcry_xcalloc_secure ((a),(b))
+#define xrealloc(a,b) gcry_xrealloc ((a),(b))
+#define xstrdup(a) gcry_xstrdup ((a))
+
+/* For compatibility with gpg 1.4 we also define these: */
+#define xmalloc_clear(a) gcry_xcalloc (1, (a))
+#define xmalloc_secure_clear(a) gcry_xcalloc_secure (1, (a))
+
+
+/* A type to hold the ISO time. Note that this this is the same as
+ the the KSBA type ksba_isotime_t. */
+typedef char gnupg_isotime_t[16];
+
+
+/*-- maperror.c --*/
+int map_kbx_err (int err);
+gpg_error_t map_assuan_err_with_source (int source, int err);
+int map_to_assuan_status (int rc);
+
+/*-- gettime.c --*/
+time_t gnupg_get_time (void);
+void gnupg_get_isotime (gnupg_isotime_t timebuf);
+void gnupg_set_time (time_t newtime, int freeze);
+int gnupg_faked_time_p (void);
+u32 make_timestamp (void);
+u32 scan_isodatestr (const char *string);
+u32 add_days_to_timestamp (u32 stamp, u16 days);
+const char *strtimevalue (u32 stamp);
+const char *strtimestamp (u32 stamp); /* GMT */
+const char *isotimestamp (u32 stamp); /* GMT */
+const char *asctimestamp (u32 stamp); /* localized */
+
+
+/* Copy one iso ddate to another, this is inline so that we can do a
+ sanity check. */
+static inline void
+gnupg_copy_time (gnupg_isotime_t d, const gnupg_isotime_t s)
+{
+ if (*s && (strlen (s) != 15 || s[8] != 'T'))
+ BUG();
+ strcpy (d, s);
+}
+
+
+/*-- signal.c --*/
+void gnupg_init_signals (int mode, void (*fast_cleanup)(void));
+void gnupg_pause_on_sigusr (int which);
+void gnupg_block_all_signals (void);
+void gnupg_unblock_all_signals (void);
+
+/*-- yesno.c --*/
+int answer_is_yes (const char *s);
+int answer_is_yes_no_default (const char *s, int def_answer);
+int answer_is_yes_no_quit (const char *s);
+int answer_is_okay_cancel (const char *s, int def_answer);
+
+/*-- xreadline.c --*/
+ssize_t read_line (FILE *fp,
+ char **addr_of_buffer, size_t *length_of_buffer,
+ size_t *max_length);
+
+
+/*-- b64enc.c --*/
+struct b64state
+{
+ unsigned int flags;
+ int idx;
+ int quad_count;
+ FILE *fp;
+ char *title;
+ unsigned char radbuf[4];
+};
+gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title);
+gpg_error_t b64enc_write (struct b64state *state,
+ const void *buffer, size_t nbytes);
+gpg_error_t b64enc_finish (struct b64state *state);
+
+/*-- sexputil.c */
+gpg_error_t keygrip_from_canon_sexp (const unsigned char *key, size_t keylen,
+ unsigned char *grip);
+int cmp_simple_canon_sexp (const unsigned char *a, const unsigned char *b);
+unsigned char *make_simple_sexp_from_hexstr (const char *line,
+ size_t *nscanned);
+
+/*-- homedir.c --*/
+const char *default_homedir (void);
+
+
+/*-- miscellaneous.c --*/
+
+/* Same as asprintf but return an allocated buffer suitable to be
+ freed using xfree. This function simply dies on memory failure,
+ thus no extra check is required. */
+char *xasprintf (const char *fmt, ...) JNLIB_GCC_A_PRINTF(1,2);
+/* Same as asprintf but return an allocated buffer suitable to be
+ freed using xfree. This function returns NULL on memory failure and
+ sets errno. */
+char *xtryasprintf (const char *fmt, ...) JNLIB_GCC_A_PRINTF(1,2);
+
+const char *print_fname_stdout (const char *s);
+const char *print_fname_stdin (const char *s);
+void print_string (FILE *fp, const byte *p, size_t n, int delim);
+void print_utf8_string2 ( FILE *fp, const byte *p, size_t n, int delim);
+void print_utf8_string (FILE *fp, const byte *p, size_t n);
+char *make_printable_string (const void *p, size_t n, int delim);
+
+int is_file_compressed (const char *s, int *ret_rc);
+
+int match_multistr (const char *multistr,const char *match);
+
+
+/*-- Simple replacement functions. */
+#ifndef HAVE_TTYNAME
+/* Systems without ttyname (W32) will merely return NULL. */
+static inline char *
+ttyname (int fd)
+{
+ return NULL
+};
+#endif /* !HAVE_TTYNAME */
+
+#ifndef HAVE_ISASCII
+static inline int
+isascii (int c)
+{
+ return (((c) & ~0x7f) == 0);
+}
+#endif /* !HAVE_ISASCII */
+
+/*-- Macros to replace ctype ones to avoid locale problems. --*/
+#define spacep(p) (*(p) == ' ' || *(p) == '\t')
+#define digitp(p) (*(p) >= '0' && *(p) <= '9')
+#define hexdigitp(a) (digitp (a) \
+ || (*(a) >= 'A' && *(a) <= 'F') \
+ || (*(a) >= 'a' && *(a) <= 'f'))
+ /* Note this isn't identical to a C locale isspace() without \f and
+ \v, but works for the purposes used here. */
+#define ascii_isspace(a) ((a)==' ' || (a)=='\n' || (a)=='\r' || (a)=='\t')
+
+/* The atoi macros assume that the buffer has only valid digits. */
+#define atoi_1(p) (*(p) - '0' )
+#define atoi_2(p) ((atoi_1(p) * 10) + atoi_1((p)+1))
+#define atoi_4(p) ((atoi_2(p) * 100) + atoi_2((p)+2))
+#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
+ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
+#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
+#define xtoi_4(p) ((xtoi_2(p) * 256) + xtoi_2((p)+2))
+
+
+
+#endif /*GNUPG_COMMON_UTIL_H*/
diff --git a/common/vasprintf.c b/common/vasprintf.c
new file mode 100644
index 000000000..4bed8de66
--- /dev/null
+++ b/common/vasprintf.c
@@ -0,0 +1,169 @@
+/* Like vsprintf but provides a pointer to malloc'd storage, which must
+ be freed by the caller.
+ Copyright (C) 1994, 2002 Free Software Foundation, Inc.
+
+This file is part of the libiberty library.
+Libiberty is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public
+License as published by the Free Software Foundation; either
+version 2 of the License, or (at your option) any later version.
+
+Libiberty 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
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with libiberty; see the file COPYING.LIB. If
+not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+Fifth Floor, Boston, MA 02110-1301, USA. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#ifdef TEST
+int global_total_width;
+#endif
+
+int
+vasprintf (char **result, const char *format, va_list args)
+{
+ const char *p = format;
+ /* Add one to make sure that it is never zero, which might cause malloc
+ to return NULL. */
+ int total_width = strlen (format) + 1;
+ va_list ap;
+
+#ifdef va_copy
+ va_copy (ap, args);
+#else
+#ifdef __va_copy
+ __va_copy (ap, args);
+#else
+ memcpy (&ap, args, sizeof (va_list));
+#endif /* __va_copy */
+#endif /* va_copy */
+
+ while (*p != '\0')
+ {
+ if (*p++ == '%')
+ {
+ while (strchr ("-+ #0", *p))
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ total_width += strtoul (p, (char**)&p, 10);
+ if (*p == '.')
+ {
+ ++p;
+ if (*p == '*')
+ {
+ ++p;
+ total_width += abs (va_arg (ap, int));
+ }
+ else
+ total_width += strtoul (p, (char**)&p, 10);
+ }
+ while (strchr ("hlL", *p))
+ ++p;
+ /* Should be big enough for any format specifier except %s
+ and floats. */
+ total_width += 30;
+ switch (*p)
+ {
+ case 'd':
+ case 'i':
+ case 'o':
+ case 'u':
+ case 'x':
+ case 'X':
+ case 'c':
+ (void) va_arg (ap, int);
+ break;
+ case 'f':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ (void) va_arg (ap, double);
+ /* Since an ieee double can have an exponent of 307, we'll
+ make the buffer wide enough to cover the gross case. */
+ total_width += 307;
+ break;
+ case 's':
+ {
+ char *tmp = va_arg (ap, char *);
+ if (tmp)
+ total_width += strlen (tmp);
+ else /* in case the vsprintf does prints a text */
+ total_width += 25; /* e.g. "(null pointer reference)" */
+ }
+ break;
+ case 'p':
+ case 'n':
+ (void) va_arg (ap, char *);
+ break;
+ }
+ }
+ }
+#ifdef TEST
+ global_total_width = total_width;
+#endif
+ *result = malloc (total_width);
+ if (*result != NULL)
+ return vsprintf (*result, format, args);
+ else
+ return 0;
+}
+
+
+int
+asprintf (char **buf, const char *fmt, ...)
+{
+ int status;
+ va_list ap;
+
+ va_start (ap, fmt);
+ status = vasprintf (buf, fmt, ap);
+ va_end (ap);
+ return status;
+}
+
+
+#ifdef TEST
+void
+checkit (const char* format, ...)
+{
+ va_list args;
+ char *result;
+
+ va_start (args, format);
+ vasprintf (&result, format, args);
+ if (strlen (result) < global_total_width)
+ printf ("PASS: ");
+ else
+ printf ("FAIL: ");
+ printf ("%d %s\n", global_total_width, result);
+}
+
+int
+main (void)
+{
+ checkit ("%d", 0x12345678);
+ checkit ("%200d", 5);
+ checkit ("%.300d", 6);
+ checkit ("%100.150d", 7);
+ checkit ("%s", "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
+777777777777777777333333333333366666666666622222222222777777777777733333");
+ checkit ("%f%s%d%s", 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx");
+}
+#endif /* TEST */
diff --git a/common/w32reg.c b/common/w32reg.c
new file mode 100644
index 000000000..84308e916
--- /dev/null
+++ b/common/w32reg.c
@@ -0,0 +1,174 @@
+/* w32reg.c - MS-Windows Registry access
+ * Copyright (C) 1999, 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#ifdef HAVE_W32_SYSTEM
+ /* This module is only used in this environment */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <windows.h>
+
+#include "util.h"
+#include "sysutils.h"
+
+static HKEY
+get_root_key(const char *root)
+{
+ HKEY root_key;
+
+ if( !root )
+ root_key = HKEY_CURRENT_USER;
+ else if( !strcmp( root, "HKEY_CLASSES_ROOT" ) )
+ root_key = HKEY_CLASSES_ROOT;
+ else if( !strcmp( root, "HKEY_CURRENT_USER" ) )
+ root_key = HKEY_CURRENT_USER;
+ else if( !strcmp( root, "HKEY_LOCAL_MACHINE" ) )
+ root_key = HKEY_LOCAL_MACHINE;
+ else if( !strcmp( root, "HKEY_USERS" ) )
+ root_key = HKEY_USERS;
+ else if( !strcmp( root, "HKEY_PERFORMANCE_DATA" ) )
+ root_key = HKEY_PERFORMANCE_DATA;
+ else if( !strcmp( root, "HKEY_CURRENT_CONFIG" ) )
+ root_key = HKEY_CURRENT_CONFIG;
+ else
+ return NULL;
+
+ return root_key;
+}
+
+
+/****************
+ * Return a string from the Win32 Registry or NULL in case of
+ * error. Caller must release the return value. A NULL for root
+ * is an alias for HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE in turn.
+ * NOTE: The value is allocated with a plain malloc() - use free() and not
+ * the usual m_free()!!!
+ */
+char *
+read_w32_registry_string( const char *root, const char *dir, const char *name )
+{
+ HKEY root_key, key_handle;
+ DWORD n1, nbytes, type;
+ char *result = NULL;
+
+ if ( !(root_key = get_root_key(root) ) )
+ return NULL;
+
+ if( RegOpenKeyEx( root_key, dir, 0, KEY_READ, &key_handle ) )
+ {
+ if (root)
+ return NULL; /* no need for a RegClose, so return direct */
+ /* It seems to be common practise to fall back to HLM. */
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, dir, 0, KEY_READ, &key_handle) )
+ return NULL; /* still no need for a RegClose, so return direct */
+ }
+
+ nbytes = 1;
+ if( RegQueryValueEx( key_handle, name, 0, NULL, NULL, &nbytes ) )
+ goto leave;
+ result = malloc( (n1=nbytes+1) );
+ if( !result )
+ goto leave;
+ if( RegQueryValueEx( key_handle, name, 0, &type, result, &n1 ) ) {
+ free(result); result = NULL;
+ goto leave;
+ }
+ result[nbytes] = 0; /* make sure it is really a string */
+ if (type == REG_EXPAND_SZ && strchr (result, '%')) {
+ char *tmp;
+
+ n1 += 1000;
+ tmp = malloc (n1+1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1) {
+ free (tmp);
+ n1 = nbytes;
+ tmp = malloc (n1 + 1);
+ if (!tmp)
+ goto leave;
+ nbytes = ExpandEnvironmentStrings (result, tmp, n1);
+ if (nbytes && nbytes > n1) {
+ free (tmp); /* oops - truncated, better don't expand at all */
+ goto leave;
+ }
+ tmp[nbytes] = 0;
+ free (result);
+ result = tmp;
+ }
+ else if (nbytes) { /* okay, reduce the length */
+ tmp[nbytes] = 0;
+ free (result);
+ result = malloc (strlen (tmp)+1);
+ if (!result)
+ result = tmp;
+ else {
+ strcpy (result, tmp);
+ free (tmp);
+ }
+ }
+ else { /* error - don't expand */
+ free (tmp);
+ }
+ }
+
+ leave:
+ RegCloseKey( key_handle );
+ return result;
+}
+
+
+int
+write_w32_registry_string(const char *root, const char *dir,
+ const char *name, const char *value)
+{
+ HKEY root_key, reg_key;
+
+ if ( !(root_key = get_root_key(root) ) )
+ return -1;
+
+ if ( RegOpenKeyEx( root_key, dir, 0, KEY_WRITE, &reg_key )
+ != ERROR_SUCCESS )
+ return -1;
+
+ if ( RegSetValueEx( reg_key, name, 0, REG_SZ, (BYTE *)value,
+ strlen( value ) ) != ERROR_SUCCESS ) {
+ if ( RegCreateKey( root_key, name, &reg_key ) != ERROR_SUCCESS ) {
+ RegCloseKey(reg_key);
+ return -1;
+ }
+ if ( RegSetValueEx( reg_key, name, 0, REG_SZ, (BYTE *)value,
+ strlen( value ) ) != ERROR_SUCCESS ) {
+ RegCloseKey(reg_key);
+ return -1;
+ }
+ }
+
+ RegCloseKey( reg_key );
+
+ return 0;
+}
+
+#endif /*HAVE_W32_SYSTEM*/
diff --git a/common/xasprintf.c b/common/xasprintf.c
new file mode 100644
index 000000000..75ae18072
--- /dev/null
+++ b/common/xasprintf.c
@@ -0,0 +1,63 @@
+/* xasprintf.c
+ * Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "util.h"
+#include "iobuf.h"
+
+/* Same as asprintf but return an allocated buffer suitable to be
+ freed using xfree. This function simply dies on memory failure,
+ thus no extra check is required. */
+char *
+xasprintf (const char *fmt, ...)
+{
+ va_list ap;
+ char *buf, *p;
+
+ va_start (ap, fmt);
+ if (vasprintf (&buf, fmt, ap) < 0)
+ log_fatal ("asprintf failed: %s\n", strerror (errno));
+ va_end (ap);
+ p = xstrdup (buf);
+ free (buf);
+ return p;
+}
+
+/* Same as above but return NULL on memory failure. */
+char *
+xtryasprintf (const char *fmt, ...)
+{
+ int rc;
+ va_list ap;
+ char *buf, *p;
+
+ va_start (ap, fmt);
+ rc = vasprintf (&buf, fmt, ap);
+ va_end (ap);
+ if (rc < 0)
+ return NULL;
+ p = xtrystrdup (buf);
+ free (buf);
+ return p;
+}
diff --git a/common/xreadline.c b/common/xreadline.c
new file mode 100644
index 000000000..8400df330
--- /dev/null
+++ b/common/xreadline.c
@@ -0,0 +1,117 @@
+/* xreadline.c - fgets replacement function
+ * Copyright (C) 1999, 2004 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "util.h"
+
+
+/* Same as fgets() but if the provided buffer is too short a larger
+ one will be allocated. This is similar to getline. A line is
+ considered a byte stream ending in a LF.
+
+ If MAX_LENGTH is not NULL, it shall point to a value with the
+ maximum allowed allocation.
+
+ Returns the length of the line. EOF is indicated by a line of
+ length zero. A truncated line is indicated my setting the value at
+ MAX_LENGTH to 0. If the returned value is less then 0 not enough
+ memory was enable and ERRNO is set accordingly.
+
+ If a line has been truncated, the file pointer is moved forward to
+ the end of the line so that the next read start with the next
+ line. Note that MAX_LENGTH must be re-initialzied in this case..
+
+ Note: The returned buffer is allocated with enough extra space to
+ append a CR,LF,Nul
+ */
+ssize_t
+read_line (FILE *fp,
+ char **addr_of_buffer, size_t *length_of_buffer,
+ size_t *max_length)
+{
+ int c;
+ char *buffer = *addr_of_buffer;
+ size_t length = *length_of_buffer;
+ size_t nbytes = 0;
+ size_t maxlen = max_length? *max_length : 0;
+ char *p;
+
+ if (!buffer)
+ { /* No buffer given - allocate a new one. */
+ length = 256;
+ buffer = xtrymalloc (length);
+ *addr_of_buffer = buffer;
+ if (!buffer)
+ {
+ *length_of_buffer = 0;
+ if (max_length)
+ *max_length = 0;
+ return -1;
+ }
+ *length_of_buffer = length;
+ }
+
+ length -= 3; /* Reserve 3 bytes for CR,LF,EOL. */
+ p = buffer;
+ while ((c = getc (fp)) != EOF)
+ {
+ if (nbytes == length)
+ { /* Enlarge the buffer. */
+ if (maxlen && length > maxlen) /* But not beyond our limit. */
+ {
+ /* Skip the rest of the line. */
+ while (c != '\n' && (c=getc (fp)) != EOF)
+ ;
+ *p++ = '\n'; /* Always append a LF (we reserved some space). */
+ nbytes++;
+ if (max_length)
+ *max_length = 0; /* Indicate truncation. */
+ break; /* the while loop. */
+ }
+ length += 3; /* Adjust for the reserved bytes. */
+ length += length < 1024? 256 : 1024;
+ *addr_of_buffer = xtryrealloc (buffer, length);
+ if (!*addr_of_buffer)
+ {
+ int save_errno = errno;
+ xfree (buffer);
+ *length_of_buffer = *max_length = 0;
+ errno = save_errno;
+ return -1;
+ }
+ buffer = *addr_of_buffer;
+ *length_of_buffer = length;
+ length -= 3;
+ p = buffer + nbytes;
+ }
+ *p++ = c;
+ nbytes++;
+ if (c == '\n')
+ break;
+ }
+ *p = 0; /* Make sure the line is a string. */
+
+ return nbytes;
+}
diff --git a/common/yesno.c b/common/yesno.c
new file mode 100644
index 000000000..9ca513740
--- /dev/null
+++ b/common/yesno.c
@@ -0,0 +1,139 @@
+/* yesno.c - Yes/No questions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "i18n.h"
+#include "util.h"
+
+int
+answer_is_yes_no_default( const char *s, int def_answer )
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_yes = _("yes");
+ const char *short_yes = _("yY");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_no = _("no");
+ const char *short_no = _("nN");
+
+ /* Note: we have to use the local dependent compare here. */
+ if ( match_multistr(long_yes,s) )
+ return 1;
+ if ( *s && strchr( short_yes, *s ) && !s[1] )
+ return 1;
+ /* Test for "no" strings to catch ambiguities for the next test. */
+ if ( match_multistr(long_no,s) )
+ return 0;
+ if ( *s && strchr( short_no, *s ) && !s[1] )
+ return 0;
+ /* Test for the english version (for those who are used to type yes). */
+ if ( !ascii_strcasecmp(s, "yes" ) )
+ return 1;
+ if ( *s && strchr( "yY", *s ) && !s[1] )
+ return 1;
+ return def_answer;
+}
+
+int
+answer_is_yes ( const char *s )
+{
+ return answer_is_yes_no_default(s,0);
+}
+
+/****************
+ * Return 1 for yes, -1 for quit, or 0 for no
+ */
+int
+answer_is_yes_no_quit ( const char *s )
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_yes = _("yes");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_no = _("no");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_quit = _("quit");
+ const char *short_yes = _("yY");
+ const char *short_no = _("nN");
+ const char *short_quit = _("qQ");
+
+ /* Note: we have to use a local dependent compare here. */
+ if ( match_multistr(long_no,s) )
+ return 0;
+ if ( match_multistr(long_yes,s) )
+ return 1;
+ if ( match_multistr(long_quit,s) )
+ return -1;
+ if ( *s && strchr( short_no, *s ) && !s[1] )
+ return 0;
+ if ( *s && strchr( short_yes, *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( short_quit, *s ) && !s[1] )
+ return -1;
+ /* but not here. */
+ if ( !ascii_strcasecmp(s, "yes" ) )
+ return 1;
+ if ( !ascii_strcasecmp(s, "quit" ) )
+ return -1;
+ if ( *s && strchr( "yY", *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( "qQ", *s ) && !s[1] )
+ return -1;
+ return 0;
+}
+
+/*
+ Return 1 for okay, 0 for for cancel or DEF_ANSWER for default.
+ */
+int
+answer_is_okay_cancel (const char *s, int def_answer)
+{
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_okay = _("okay|okay");
+ /* TRANSLATORS: See doc/TRANSLATE about this string. */
+ const char *long_cancel = _("cancel|cancel");
+ const char *short_okay = _("oO");
+ const char *short_cancel = _("cC");
+
+ /* Note: We have to use the locale dependent compare. */
+ if ( match_multistr(long_okay,s) )
+ return 1;
+ if ( match_multistr(long_cancel,s) )
+ return 0;
+ if ( *s && strchr( short_okay, *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( short_cancel, *s ) && !s[1] )
+ return 0;
+ /* Always test for the English values (not locale here). */
+ if ( !ascii_strcasecmp(s, "okay" ) )
+ return 1;
+ if ( !ascii_strcasecmp(s, "ok" ) )
+ return 1;
+ if ( !ascii_strcasecmp(s, "cancel" ) )
+ return 0;
+ if ( *s && strchr( "oO", *s ) && !s[1] )
+ return 1;
+ if ( *s && strchr( "cC", *s ) && !s[1] )
+ return 0;
+ return def_answer;
+}
+