aboutsummaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/ChangeLog508
-rw-r--r--tools/Makefile.am72
-rw-r--r--tools/Manifest6
-rw-r--r--tools/README.gpgconf79
-rwxr-xr-xtools/addgnupghome122
-rw-r--r--tools/bftest.c108
-rw-r--r--tools/clean-sat.c34
-rwxr-xr-xtools/convert-from-10657
-rw-r--r--tools/crlf.c52
-rwxr-xr-xtools/der-to-pem27
-rw-r--r--tools/gpg-connect-agent.c639
-rw-r--r--tools/gpg-zip.in142
-rw-r--r--tools/gpgconf-comp.c2499
-rw-r--r--tools/gpgconf.c203
-rw-r--r--tools/gpgconf.h59
-rw-r--r--tools/gpgkey2ssh.c298
-rw-r--r--tools/gpgparsemail.c767
-rwxr-xr-xtools/gpgsm-gencert.sh171
-rw-r--r--tools/gpgsplit.c879
-rwxr-xr-xtools/lspgpot27
-rwxr-xr-xtools/mail-signed-keys114
-rw-r--r--tools/make-dns-cert.c239
-rw-r--r--tools/mk-tdata.c67
-rw-r--r--tools/mpicalc.c385
-rw-r--r--tools/no-libgcrypt.c112
-rwxr-xr-xtools/pgpgroup-to-gpggroup42
-rw-r--r--tools/rfc822parse.c1258
-rw-r--r--tools/rfc822parse.h81
-rwxr-xr-xtools/ring-a-party114
-rw-r--r--tools/shmtest.c201
-rw-r--r--tools/signmany33
-rw-r--r--tools/symcryptrun.c1073
-rw-r--r--tools/watchgnupg.c402
33 files changed, 8232 insertions, 2638 deletions
diff --git a/tools/ChangeLog b/tools/ChangeLog
index 00455c619..25ffae5d2 100644
--- a/tools/ChangeLog
+++ b/tools/ChangeLog
@@ -1,219 +1,479 @@
-2006-04-20 David Shaw <[email protected]>
+2006-06-09 Marcus Brinkmann <[email protected]>
- * make-dns-cert.c (main): Small exit code tweak from Peter
- Palfrader.
+ * Makefile.am (gpgconf_LDADD): Add $(GPG_ERROR_LIBS).
+ (gpgkey2ssh_LDADD): Add ../jnlib/libjnlib.a.
-2006-04-05 David Shaw <[email protected]>
+2006-05-23 Werner Koch <[email protected]>
- * make-dns-cert.c: Some changes from Peter Palfrader to send
- errors to stderr and allow spaces in a fingerprint. Also warn
- when a key is over 16k (as that is the default max-cert-size) and
- fail when a key is over 64k as that is the DNS limit in many
- places.
+ * gpgparsemail.c: Include config.h if available
+ (stpcpy): Conditional include it.
-2006-04-04 David Shaw <[email protected]>
+ * gpgconf-comp.c (hextobyte): Removed as it is now availble in
+ jnlib.
- * make-dns-cert.c: New program to generate properly formatted CERT
- records so people don't have to do it manually.
+2005-12-20 Werner Koch <[email protected]>
-2006-02-14 Werner Koch <[email protected]>
+ * gpgconf-comp.c (gc_options_gpg): Add allow-pka-lookup.
- * mk-tdata.c (main): Implement option --char.
+2005-12-14 Werner Koch <[email protected]>
-2005-08-05 David Shaw <[email protected]>
+ * Makefile.am (bin_PROGRAMS): Build gpgparsemail.
- * gpg-zip.in: Add --decrypt functionality. Fix quoting so
- filenames with spaces work properly.
+ * gpgparsemail.c (pkcs7_begin): New.
+ (parse_message, message_cb): Add support of direct pkcs signatures.
-2005-08-04 David Shaw <[email protected]>
+2005-10-19 Werner Koch <[email protected]>
- * gpg-zip.in: New. Script wrapper to work with encrypted tar
- files, a la PGP Zip.
+ * gpgconf-comp.c (gc_options_scdaemon): New option --disable-keypad.
- * Makefile.am: Use it if we have a USTAR compatible tar.
+2005-09-22 Werner Koch <[email protected]>
-2004-12-18 David Shaw <[email protected]>
+ * rfc822parse.c (parse_field): Tread Content-Disposition special.
- * Makefile.am: Link with readline where needed.
+2005-10-08 Marcus Brinkmann <[email protected]>
-2004-10-28 Werner Koch <[email protected]>
+ * Makefile.am (watchgnupg_LDADD): New variable.
- * Makefile.am (other_libs): New. Also include LIBICONV. Noted by
- Tim Mooney.
+ * Makefile.am (gpgconf_LDADD): Add ../gl/libgnu.a after
+ ../common/libcommon.a.
+ (symcryptrun_LDADD, gpg_connect_agent_LDADD, gpgkey2ssh_LDADD):
+ Likewise.
+
+2005-09-29 Marcus Brinkmann <[email protected]>
+
+ * Makefile.am (AM_CFLAGS): Add $(LIBGCRYPT_CFLAGS).
+
+2005-09-06 Werner Koch <[email protected]>
+
+ * rfc822parse.c, rfc822parse.h: Changed license to LGPL.
+
+2005-08-01 Werner Koch <[email protected]>
+
+ * gpgsm-gencert.sh: Allow entering a keygrip to generate a CSR from
+ an existing key.
+
+2005-07-21 Werner Koch <[email protected]>
+
+ * gpgsm-gencert.sh: Reworked to allow for multiple email addresses
+ as well as DNsanmes and URi. Present the parameter file before
+ creating the certificate.
+
+2005-07-04 Marcus Brinkmann <[email protected]>
+
+ * symcryptrun.c (SYMC_BAD_PASSPHRASE, SYMC_CANCELED): New symbols,
+ use instead constants.
+ (hash_string): New function copied from simple-gettext.c.
+ (confucius_get_pass): Take new argument CACHEID.
+ (confucius_process): Calculate cacheid and pass it to
+ confucius_get_pass. Clear passphrase from cache if necessary.
+
+2005-06-16 Werner Koch <[email protected]>
+
+ * gpg-connect-agent.c (read_and_print_response): Made LINELEN a
+ size_t.
+
+2005-06-04 Marcus Brinkmann <[email protected]>
+
+ * symcryptrun.c (main): Allow any number of arguments, don't use
+ first argument as input file name. Pass extra arguments to
+ confucius_main.
+ (confucius_main): Accept new arguments argc and argv and pass them
+ to confucius_process.
+ (confucius_process): Accept new arguments argc and argv and pass
+ them to the confucius process.
+
+2005-06-01 Werner Koch <[email protected]>
+
+ * symcryptrun.c: Include mkdtemp.h.
+
+2005-05-31 Werner Koch <[email protected]>
+
+ * watchgnupg.c: Make sure that PF_LCOAL and AF_LOCAL are defines.
+ Noted by Ray Link.
+
+2005-05-28 Moritz Schulte <[email protected]>
+
+ * gpgkey2ssh.c: New file.
+ * Makefile.am (bin_PROGRAMS): Added gpgkey2ssh.
+
+2005-05-20 Werner Koch <[email protected]>
+
+ * gpg-connect-agent.c (add_definq, show_definq, clear_definq)
+ (handle_inquire): New.
+ (read_and_print_response): Handle INQUIRE command.
+ (main): Implement control commands.
+
+2005-04-21 Werner Koch <[email protected]>
+
+ * symcryptrun.c (main): Optionally allow the input file as command
+ line argument.
+
+ * gpgconf-comp.c: Add gpgsm option disable-trusted-cert-crl-check.
+
+2005-04-20 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c: Add gpg-agent:disable-scdaemon.
+
+2005-04-19 Marcus Brinkmann <[email protected]>
+
+ * symcryptrun.c: Add --input option.
+
+2005-04-15 Marcus Brinkmann <[email protected]>
+
+ * symcryptrun.c (TEMP_FAILURE_RETRY): Define if not defined.
+
+ * symcryptrun.c (remove_file): New function.
+ (confucius_copy_file): Accept new argument PLAIN and shred the
+ file if it is set on error.
+
+ * Makefile.am: Define symcryptrun make variable depending on
+ BUILD_SYMCRYPTUN.
+ (bin_PROGRAMS): Add ${symcryptrun} instead symcryptrun.
+ (symcryptrun_LDADD): Use $(LIBUTIL_LIBS) instead of -lutil.
+
+2005-04-11 Werner Koch <[email protected]>
+
+ * symcryptrun.c (confucius_mktmpdir): Changed to use mkdtmp(3).
+
+2005-04-11 Marcus Brinkmann <[email protected]>
+
+ * symcryptrun.c: Implement config file parsing.
+
+ * Makefile.am (bin_PROGRAMS): Add symcryptrun.
+ (symcryptrun_SOURCES, symcryptrun_LDADD): New variables.
+ * symcryptrun.c: New file.
+
+2005-03-31 Werner Koch <[email protected]>
+
+ * gpg-connect-agent.c (start_agent): Use PATHSEP_C instead of ':'.
+
+2005-03-09 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c <dirmngr>: Add honor-http-proxy.
+
+2005-02-25 Werner Koch <[email protected]>
+
+ * no-libgcrypt.c (gcry_strdup): New.
+
+2005-02-24 Werner Koch <[email protected]>
+
+ * gpg-connect-agent.c: New.
+ * Makefile.am: Add it.
+
+2004-12-21 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c (get_config_pathname) [DOSISH]: Detect absolute
+ pathnames with a drive letter.
+
+2004-12-15 Werner Koch <[email protected]>
+
+ * Makefile.am (bin_PROGRAMS) [W32]: Do not build watchgnupg.
+
+ * gpgconf-comp.c (gpg_agent_runtime_change) [W32]: No way yet to
+ send a signal. Disable.
+ (change_options_file, change_options_program) [W32]: No link(2),
+ so we disable it.
+ (gc_component_change_options): Use rename instead of link.
+
+2004-12-13 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c <ignore-ocsp-service-url>: Fixed typo.
+
+2004-11-24 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c <dirmngr>: Add --ignore-http-dp, --ignore-ldap-dp
+ and --ignore-ocsp-service-url.
+
+2004-11-23 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c <dirmngr>: Add the proxy options.
+ <gpgsm>: Add --prefer-system-daemon.
+
+2004-11-11 Werner Koch <[email protected]>
+
+ * watchgnupg.c (main): Fixed test for read error.
+
+2004-10-22 Werner Koch <[email protected]>
+
+ * Makefile.am (bin_SCRIPTS): Add gpgsm-gencert.sh
+
+ * gpgsm-gencert.sh: Fixed copyright; its part of GnuPG thus FSF.
2004-10-01 Werner Koch <[email protected]>
- * bftest.c (i18n_init): Always use LC_ALL.
- * shmtest.c (i18n_init): Ditto.
- * mpicalc.c (i18n_init): Ditto.
+ * gpgconf-comp.c: Made all strings for --log-file read the same.
+
+2004-10-01 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c (my_dgettext): Also switch codeset and directory
+ for the other used domains (i.e. dirmngr).
+
+ * gpgconf.c (main): Fixed translation markers.
+
+2004-09-30 Werner Koch <[email protected]>
+
+ * gpgconf.c (i18n_init): Always use LC_ALL.
+
+ * Makefile.am: Adjusted for gettext 0.14.
+
+2004-09-29 Werner Koch <[email protected]>
+
+ * gpgconf-comp.c: Made the entries fro GROUPs translatable.
+ Include i18n.h.
+ (my_dgettext): Hack to use the gnupg2 domain.
-2004-09-13 David Shaw <[email protected]>
+2004-08-09 Moritz Schulte <[email protected]>
- * pgpgroup-to-gpggroup: New perl script to take groups from PGP
- command line and write out GnuPG 'group' config lines.
+ * gpgsm-gencert.sh: New file.
-2004-07-04 David Shaw <[email protected]>
+2004-06-16 Werner Koch <[email protected]>
- * ring-a-party: ElGamal -> Elgamal
+ * rfc822parse.c (rfc822parse_get_field): Add arg VALUEOFF.
-2004-01-11 David Shaw <[email protected]>
+2004-06-14 Werner Koch <[email protected]>
- * convert-from-106, lspgpot: Check for gpg binary before
- proceeding. Don't hardcode the path to gpg.
+ * no-libgcrypt.c (gcry_realloc, gcry_xmalloc, gcry_xcalloc): New.
- * gpgsplit.c (handle_bzip2): Remove two cut and paste typecast
- errors. Noted by Stefan Bellon.
+ * gpgconf-comp.c (retrieve_options_from_program)
+ (retrieve_options_from_file, change_options_file)
+ (change_options_program, gc_component_change_options): Replaced
+ getline by read_line and test for allocation failure.
-2003-12-28 Stefan Bellon <[email protected]>
+2004-05-21 Marcus Brinkmann <[email protected]>
- * gpgsplit.c [__riscos__]: Removal of unnecessary #ifdef
- __riscos__ sections.
+ * gpgconf-comp.c (gc_options_dirmngr): Remove CRL group, put its
+ only option "max-replies" into LDAP group.
+ (gc_component): Change description of dirmngr to "Directory
+ Manager".
-2003-12-06 David Shaw <[email protected]>
+ * gpgconf-comp.c (gc_component_change_options): Move the
+ per-process backup file into a standard location.
- * gpgsplit.c (write_part): Split off decompression code.
- (handle_zlib): Move it here.
- (handle_bzip2): Add this to handle BZIP2 compressed messages.
+2004-05-03 Werner Koch <[email protected]>
-2003-10-25 Werner Koch <[email protected]>
+ * gpgconf-comp.c: Add --allow-mark-trusted for the gpg-agent.
- * Makefile.am: Replaced INTLLIBS by LIBINTL.
+2004-04-30 Werner Koch <[email protected]>
-2003-08-24 David Shaw <[email protected]>
+ * gpgconf-comp.c: Added more runtime flags for the gpg-agent
+ backend.
- * Makefile.am: Use NETLIBS instead of EGDLIBS.
+2004-04-29 Marcus Brinkmann <[email protected]>
-2003-07-10 David Shaw <[email protected]>
+ * gpgconf-comp.c (change_options_program): Turn on utf8-strings in
+ the gpgconf specific part of the config file for the GnuPG
+ backend.
- * Makefile.am: Use W32LIBS where appropriate.
+2004-04-28 Werner Koch <[email protected]>
-2003-05-30 David Shaw <[email protected]>
+ * gpgconf-comp.c: Add --ocsp-signer for the dirmngr backend.
- * Makefile.am: Some cleanup, and use DLLIBS for -ldl.
+2004-04-20 Marcus Brinkmann <[email protected]>
-2003-05-24 David Shaw <[email protected]>
+ * gpgconf-comp.c (gc_options_gpg_agent): Change type of
+ ignore-cache-for-signing option to GC_ARG_TYPE_NONE.
- * bftest.c, crlf.c, mk-tdata.c, mpicalc.c, shmtest.c: Edit all
- preprocessor instructions to remove whitespace before the
- '#'. This is not required by C89, but there are some compilers out
- there that don't like it.
+2004-04-07 Werner Koch <[email protected]>
-2003-03-11 David Shaw <[email protected]>
+ * gpgconf-comp.c (my_dgettext): Switch the codeset once to utf-8.
+ Allow building with out NLS.
- * Makefile.am: Use @CAPLIBS@ to link in -lcap if we are using
- capabilities.
+2004-03-23 Marcus Brinkmann <[email protected]>
-2003-02-22 David Shaw <[email protected]>
+ * gpgconf-comp.c (gc_options_dirmngr): Set GC_OPT_FLAG_ARG_OPT for
+ "LDAP Server".
+ (change_options_file): Remove assertion that tests that this flag
+ is not present. Handle an empty string in OPTION->new_value.
- * Makefile.am: Distribute convert-from-106.
+ * gpgconf.c (main): Remove obsolete warning.
- * convert-from-106: Script to automate the 1.0.6->later
- conversion. It marks all secret keys as ultimately trusted, adds
- the signature caches, and checks the trustdb. Moved from the
- scripts directory.
+2004-03-23 Werner Koch <[email protected]>
-2002-10-31 Stefan Bellon <[email protected]>
+ * gpgconf-comp.c (gc_options_gpg): New.
+ (gc_component_t, gc_component): Add GC_BACKEND_GPG.
+ (gc_options_dirmngr): Add allow-ocsp.
- * gpgsplit.c (write_part) [__riscos__]: Use riscos_load_module()
- to load ZLib module.
+2004-03-23 Marcus Brinkmann <[email protected]>
-2002-10-23 Werner Koch <[email protected]>
+ * gpgconf-comp.c (gc_flag): Add missing flags.
- * gpgsplit.c: New options --secret-to-public and --no-split.
- GNUified the indentation style.
+ * gpgconf-comp.c: Include <signal.h>.
+ (gc_backend): Add new member runtime_change.
+ (gpg_agent_runtime_change): New function.
+ (gc_component_change_options): New variable runtime. Initialize
+ it. If an option is changed that has the GC_OPT_FLAG_RUNTIME bit
+ set, also set the corresponding runtime variable. Finally, call
+ the runtime_change callback of the backend if needed.
-2002-09-25 David Shaw <[email protected]>
+2004-03-16 Werner Koch <[email protected]>
- * Makefile.am: Link bftest with EGDLIBS (i.e. NETLIBS) as EGD uses
- sockets.
+ * gpgconf-comp.c (gc_options_gpg_agent): Implemented.
+ (gc_options_gpgsm, gc_options_scdaemon): Implemented.
+ (gc_backend_t): Add GC_BACKEND_SCDAEMON.
-2002-05-07 Stefan Bellon <[email protected]>
+2004-03-12 Marcus Brinkmann <[email protected]>
- * gpgsplit.c (create_filename): Use EXTSEP_S instead of ".".
+ * gpgconf-comp.c (gc_component_change_options): Set the filenames
+ of the option's backend, not of the component.
+ Also use GC_BACKEND_NR, not GC_COMPONENT_NR.
-2002-04-23 David Shaw <[email protected]>
+2004-03-09 Werner Koch <[email protected]>
- * Makefile.am: Do not list libraries in -lxxx format in a
- dependency line.
+ * gpgconf-comp.c [_riscos_]: Removed special code for RISC OS; we
+ don't want to clutter our code with system dependent stuff.
-2002-01-02 Stefan Bellon <[email protected]>
+2004-03-08 Marcus Brinkmann <[email protected]>
- * gpgsplit.c [__riscos__]: Added RISC OS specific file name
- code.
+ * gpgconf-comp.c (retrieve_options_from_file): Quote each string
+ in the list, not only the first.
- * gpgsplit.c (write_part): Introduced two explicit casts.
+2004-02-26 Marcus Brinkmann <[email protected]>
-2001-12-21 David Shaw <[email protected]>
+ * gpgconf-comp.c (gc_component_list_options): Do not print empty
+ groups.
- * gpgsplit.c (pkttype_to_string): PKT_PHOTO_ID -> PKT_ATTRIBUTE
+ * gpgconf-comp.c (option_check_validity): Check if option is
+ active.
+ (change_options_file): Implement.
-2001-10-23 Werner Koch <[email protected]>
+ * gpgconf-comp.c (retrieve_options_from_program): Remove broken
+ string handling.
- * Makefile.am (gpgsplit_LDADD): Add ZLIBS.
+ * gpgconf-comp.c (change_options_program): Support all types of
+ options, including list types.
-2001-09-18 Werner Koch <[email protected]>
+ * README.gpgconf: Fix description of arguments.
+ * gpgconf-comp.c (option_check_validity): Rewritten to properly
+ support optional arguments in lists.
- * gpgsplit.c: New option --uncompress.
- (write_part): Handle old style uncompressing.
+ * README.gpgconf: Add info about optional arg and arg type 0.
+ * gpgconf-comp.c (gc_component_change_options): Parse list of
+ arg type 0 options.
+ (option_check_validity): Add new argument NEW_VALUE_NR. Perform
+ rigorous validity checks.
+ (change_options_program): Disable an option also if we have a new
+ value for it.
-2001-06-20 Werner Koch <[email protected]>
+2004-02-25 Marcus Brinkmann <[email protected]>
- * gpgsplit.c: New.
- * Makefile.am (bin_PROGRAMS): Install gpgsplit.
+ * gpgconf-comp.c (gc_component_list_options): Correct output for
+ lists of arg type none.
+ (struct gc_option): Add new member new_flags.
+ (option_check_validity): Check OPTION->new_flags beside
+ OPTION->new_value. Add new argument FLAGS.
+ (gc_component_change_options): Support default flag correctly.
+ (change_options_program): Likewise.
-2001-03-27 Werner Koch <[email protected]>
+2004-02-24 Marcus Brinkmann <[email protected]>
- * mail-signed-keys: Add option --dry-run.
+ * README.gpgconf: Revert last change. Add new flags "default",
+ "default desc" and "no arg desc". Add new field ARGDEF. Add new
+ field FLAG to backend interface.
+ * gpgconf-comp.c (struct gc_option): Make flags of type unsigned
+ long.
+ (gc_component_list_options): Adjust type for flags.
+ Add default argument field.
+ (retrieve_options_from_program): Use "1" as value for non-option
+ arguments, not "Y".
+ (gc_component_change_options): Read in flags from input.
-2001-03-21 Werner Koch <[email protected]>
+2004-02-23 Marcus Brinkmann <[email protected]>
- * shmtest.c: Add sys/types.h
+ * README.gpgconf: Change meaning of type 0 options value if it is
+ the empty string or "0".
-Fri Sep 15 18:40:36 CEST 2000 Werner Koch <[email protected]>
+ * gpgconf.h (struct): Add member runtime.
+ * gpgconf.c: Add new option oRuntime.
+ (main): Same here.
- * ring-a-party: An array start at offset 1 no 0. Many thanks to Mike
- for finding this bug.
+ * gpgconf-comp.c (hextobyte): New function.
+ (percent_deescape): New function.
+ (get_config_pathname): Percent deescape pathname if taken from
+ option (default) value. Use default value only if it exists and
+ is not empty. Use empty string otherwise. Don't include leading
+ quote in pathname.
+ (change_options_program): Percent deescape string before writing
+ it out.
+
+ * gpgconf-comp.c (gc_component_list_options): Do not skip groups
+ on output.
-Thu Sep 14 14:20:38 CEST 2000 Werner Koch <[email protected]>
+2004-02-18 Werner Koch <[email protected]>
- * ring-a-party: Flush the last key.
+ * gpgconf-comp.c: Added empty components for gpgsm and scdaemon.
-Wed Jul 5 13:28:45 CEST 2000 Werner Koch <wk@>
+2004-02-12 Werner Koch <[email protected]>
- * mail-signed-keys: New.
+ * watchgnupg.c (main): Implement option "--".
+ (print_version): New.
-Tue May 23 09:19:00 CEST 2000 Werner Koch <[email protected]>
+ * Makefile.am: Include cmacros.am for common flags.
- * ring-a-party: New.
+2004-02-03 Werner Koch <[email protected]>
-Thu Jul 8 16:21:27 CEST 1999 Werner Koch <[email protected]>
+ * addgnupghome: Try to use getent, so that it also works for NIS
+ setups.
- * lspgpot: New
+2004-01-31 Marcus Brinkmann <[email protected]>
-Wed Jul 7 13:08:40 CEST 1999 Werner Koch <[email protected]>
+ * gpgconf-comp.c: Some bug fixes, parse only defaults from the
+ program, and read the current values from the configuration file
+ directly.
- * Makefile.am: Support for libtool.
+2004-01-30 Marcus Brinkmann <[email protected]>
-Tue Jun 8 13:36:25 CEST 1999 Werner Koch <[email protected]>
+ * gpgconf-comp.c (gc_error): New function, use it instead of
+ error() throughout.
- * mpicalc.c (main): hex digits may now be lowercase
- (do_mulm): new.
+ * gpgconf-comp.c: Use xmalloc, libcommon's asctimestamp and
+ gnupg_get_time, fix error() invocation and use getline()
+ consistently.
+
+2004-01-30 Werner Koch <[email protected]>
-Thu Dec 10 20:15:36 CET 1998 Werner Koch <[email protected]>
+ * addgnupghome: Also set the group of copied files.
- * mpicalc.c (main): Moved initialization out of definition.
+2004-01-30 Werner Koch <[email protected]>
-Mon May 18 15:39:22 1998 Werner Koch ([email protected])
+ * Makefile.am (sbin_SCRIPTS): New, to install addgnupghome.
+ (EXTRA_DIST): Added rfc822parse.c rfc822parse.h gpgparsemail.c
+ which might be useful for debugging.
- * mk-tdata.c: New.
+2004-01-29 Werner Koch <[email protected]>
-Tue Apr 7 19:50:41 1998 Werner Koch ([email protected])
+ * addgnupghome: New.
- * bftest.c: Now supports all availabe ciphers.
+2004-01-29 Marcus Brinkmann <[email protected]>
+ * gpgconf-list.c: File removed.
+ * README.gpgconf: New file.
+ * gpgconf-comp.c: New file.
+ * Makefile.am (gpgconf_SOURCES): Remove gpgconf-list.c, add
+ gpgconf-comp.c.
+2004-01-16 Werner Koch <[email protected]>
- Copyright 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ * watchgnupg.c (main): Need to use FD_ISSET for the client
+ descriptors too; aiiih. Set the listening socket to non-blocking.
+
+2004-01-10 Werner Koch <[email protected]>
+
+ * Makefile.am: Use GPG_ERROR_CFLAGS
+
+2004-01-05 Werner Koch <[email protected]>
+
+ * Manifest: New.
+ * gpgconf.c, gpgconf.h, gpgconf-list.c: New. A skeleton for now.
+ * no-libgcrypt.c: New.
+ * Makefile.am: Add above.
+
+2003-12-23 Werner Koch <[email protected]>
+
+ * Makefile.am: New.
+ * watchgnupg.c: New.
+
+
+ Copyright 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
@@ -222,5 +482,3 @@ Tue Apr 7 19:50:41 1998 Werner Koch ([email protected])
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/tools/Makefile.am b/tools/Makefile.am
index 5efe2c1a6..6b4767a79 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,8 +1,8 @@
-# Copyright (C) 1998, 1999, 2000, 2001, 2003,
-# 2004 Free Software Foundation, Inc.
-#
+# Makefile.am - Tools directory
+# 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
@@ -15,25 +15,59 @@
#
# 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
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+EXTRA_DIST = Manifest watchgnupg.c \
+ addgnupghome gpgsm-gencert.sh
-## Process this file with automake to produce Makefile.in
+AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
+include $(top_srcdir)/am/cmacros.am
-EXTRA_DIST = lspgpot ring-a-party mail-signed-keys convert-from-106
-INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/intl
-needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a
-other_libs = $(LIBICONV) $(LIBINTL) $(CAPLIBS)
+AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) $(LIBASSUAN_CFLAGS)
-bin_PROGRAMS = gpgsplit
-noinst_PROGRAMS = mpicalc bftest clean-sat mk-tdata shmtest make-dns-cert
+sbin_SCRIPTS = addgnupghome
+
+bin_SCRIPTS = gpgsm-gencert.sh
+
+if BUILD_SYMCRYPTRUN
+ symcryptrun = symcryptrun
+else
+ symcryptrun =
+endif
-if HAVE_USTAR
-bin_SCRIPTS = gpg-zip
+bin_PROGRAMS = gpgconf gpg-connect-agent gpgkey2ssh ${symcryptrun} gpgparsemail
+if !HAVE_W32_SYSTEM
+bin_PROGRAMS += watchgnupg
endif
-gpgsplit_LDADD = $(needed_libs) $(other_libs) @ZLIBS@
-mpicalc_LDADD = $(needed_libs) $(other_libs) @W32LIBS@
-bftest_LDADD = $(needed_libs) $(other_libs) @W32LIBS@ @DLLIBS@ @NETLIBS@ @LIBREADLINE@
-shmtest_LDADD = $(needed_libs) $(other_libs) @LIBREADLINE@
+gpgconf_SOURCES = gpgconf.c gpgconf.h gpgconf-comp.c no-libgcrypt.c
+
+# jnlib/common sucks in gpg-error, will they, nil they (some compilers
+# do not eliminate the supposed-to-be-unused-inline-functions).
+gpgconf_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
+ ../gl/libgnu.a @LIBINTL@ $(GPG_ERROR_LIBS)
+
+gpgparsemail_SOURCES = gpgparsemail.c rfc822parse.c rfc822parse.h
+gpgparsemail_LDADD =
+
+symcryptrun_SOURCES = symcryptrun.c
+symcryptrun_LDADD = $(LIBUTIL_LIBS) ../jnlib/libjnlib.a \
+ ../common/libcommon.a ../gl/libgnu.a \
+ ../common/libsimple-pwquery.a $(LIBGCRYPT_LIBS) \
+ $(GPG_ERROR_LIBS) $(LIBINTL)
+
+watchgnupg_SOURCES = watchgnupg.c
+watchgnupg_LDADD = $(NETLIBS)
+
+gpg_connect_agent_SOURCES = gpg-connect-agent.c no-libgcrypt.c
+gpg_connect_agent_LDADD = ../jnlib/libjnlib.a \
+ ../common/libcommon.a ../gl/libgnu.a \
+ $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(LIBINTL)
-gpgsplit mpicalc bftest shmtest: $(needed_libs)
+gpgkey2ssh_SOURCES = gpgkey2ssh.c
+gpgkey2ssh_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS)
+# common sucks in jnlib, via use of BUG() in an inline function, which
+# some compilers do not eliminate.
+gpgkey2ssh_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a ../gl/libgnu.a \
+ $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS)
diff --git a/tools/Manifest b/tools/Manifest
new file mode 100644
index 000000000..96423352d
--- /dev/null
+++ b/tools/Manifest
@@ -0,0 +1,6 @@
+Makefile.am
+watchgnupg.c
+gpgconf.c
+gpgconf.h
+gpgconf-list.c
+$names$
diff --git a/tools/README.gpgconf b/tools/README.gpgconf
new file mode 100644
index 000000000..084711441
--- /dev/null
+++ b/tools/README.gpgconf
@@ -0,0 +1,79 @@
+============
+ GPG Conf
+============
+
+Main documentation for this tool can be found in doc/tools.texi.
+
+BACKENDS
+========
+
+Backends should support the following commands:
+
+Command --gpgconf-list
+----------------------
+
+List the location of the configuration file, and all default values of
+all options. The location of the configuration file must be an
+absolute pathname.
+
+The format of each line is:
+
+NAME:FLAGS:DEFAULT:ARGDEF
+
+NAME
+
+This field contains a name tag for the group or option. The name tag
+is used to specify the group or option in all communication with
+GPGConf. The name tag is to be used verbatim. It is not in any
+escaped format.
+
+FLAGS
+
+The flags field contains an unsigned number. Its value is the
+OR-wise combination of the following flag values:
+
+ 16 default If this flag is set, a default value is available.
+ 32 default desc If this flag is set, a (runtime) default is available.
+ This and the "default" flag are mutually exclusive.
+ 64 no arg desc If this flag is set, and the "optional arg" flag
+ is set, then the option has a special meaning if no
+ argument is given.
+
+DEFAULT
+
+This field is defined only for options. Its format is that of an
+option argument (see section Format Conventions for details). If the
+default value is empty, then no default is known. Otherwise, the
+value specifies the default value for this option. Note that this
+field is also meaningful if the option itself does not take a real
+argument.
+
+ARGDEF
+
+This field is defined only for options for which the "optional arg"
+flag is set. If the "no arg desc" flag is not set, its format is that
+of an option argument (see section Format Conventions for details).
+If the default value is empty, then no default is known. Otherwise,
+the value specifies the default value for this option. If the "no arg
+desc" flag is set, the field is either empty or contains a description
+of the effect of this option if no argument is given. Note that this
+field is also meaningful if the option itself does not take a real
+argument.
+
+
+Example:
+$ dirmngr --gpgconf-list
+gpgconf-config-file:/mnt/marcus/.gnupg/dirmngr.conf
+ldapservers-file:/mnt/marcus/.gnupg/dirmngr_ldapservers.conf
+add-servers:0
+max-replies:10
+
+
+TODO
+----
+
+* Implement --dry-run and --quiet.
+
+* Extend the backend interface to include gettext domain and
+description, if available, to avoid repeating this information in
+gpgconf.
diff --git a/tools/addgnupghome b/tools/addgnupghome
new file mode 100755
index 000000000..fb032b674
--- /dev/null
+++ b/tools/addgnupghome
@@ -0,0 +1,122 @@
+#!/bin/sh
+# Add a new .gnupg home directory for a list of users -*- sh -*-
+#
+# Copyright 2004 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.
+
+PGM=addgnupghome
+any_error=0
+
+
+error () {
+ echo "$PGM: $*" >&2
+ any_error=1
+}
+
+info () {
+ echo "$PGM: $*" >&2
+}
+
+# Do it for one user
+one_user () {
+ user="$1"
+ home=$(${cat_passwd} | awk -F: -v n="$user" '$1 == n {print $6}')
+ if [ -z "$home" ]; then
+ if ${cat_passwd} | awk -F: -v n="$user" '$1 == n {exit 1}'; then
+ error "no such user \`$user'"
+ else
+ error "no home directory for user \`$user'"
+ fi
+ return
+ fi
+ if [ ! -d "$home" ]; then
+ error "home directory \`$home' of user \`$user' does not exist"
+ return
+ fi
+ if [ -d "$home/.gnupg" ]; then
+ info "skipping user \`$user': \`.gnupg' already exists"
+ return
+ fi
+ info "creating home directory \`$home/.gnupg' for \`$user'"
+ if ! mkdir "$home/.gnupg" ; then
+ error "error creating \`$home/.gnupg'"
+ return
+ fi
+
+ if ! chown $user "$home/.gnupg" ; then
+ error "error changing ownership of \`$home/.gnupg'"
+ return
+ fi
+
+ group=$(id -g "$user")
+ [ -z "$group" ] && group="0"
+
+ if [ "$group" -gt 0 ]; then
+ if ! chgrp $group "$home/.gnupg" ; then
+ error "error changing group of \`$home/.gnupg'"
+ return
+ fi
+ fi
+
+ if ! cd "$home/.gnupg" ; then
+ error "error cd-ing to \`$home/.gnupg'"
+ return
+ fi
+ for f in $filelist; do
+ if [ -d /etc/skel/.gnupg/$f ]; then
+ mkdir $f
+ else
+ cp /etc/skel/.gnupg/$f $f
+ fi
+ if ! chown $user $f ; then
+ error "error changing ownership of \`$f'"
+ return
+ fi
+ if [ "$group" -gt 0 ]; then
+ if ! chgrp $group "$f" ; then
+ error "error changing group of \`$f'"
+ return
+ fi
+ fi
+ done
+
+}
+
+if [ -z "$1" ]; then
+ echo "usage: $PGM userids"
+ exit 1
+fi
+
+# Check whether we can use getent
+if getent --help </dev/null >/dev/null 2>&1 ; then
+ cat_passwd='getent passwd'
+else
+ cat_passwd='cat /etc/passwd'
+ info "please note that only users from /etc/passwd are checked"
+fi
+
+if [ ! -d /etc/skel/.gnupg ]; then
+ error "skeleton directory \`/etc/skel/.gnupg' does not exist"
+ exit 1
+fi
+cd "/etc/skel/.gnupg" || (error "error cd-ing to \`/etc/skel/.gnupg'"; exit 1)
+filelist=$(find . \( -type f -or -type d \) -not -name '*~' -not -name . -print)
+
+
+if ! umask 0077 ; then
+ error "error setting umask"
+ exit 1
+fi
+
+for name in $*; do
+ one_user $name
+done
+
+exit $any_error
diff --git a/tools/bftest.c b/tools/bftest.c
deleted file mode 100644
index 829c429a0..000000000
--- a/tools/bftest.c
+++ /dev/null
@@ -1,108 +0,0 @@
-/* bftest.c - Blowfish test program
- * 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>
-#ifdef HAVE_DOSISH_SYSTEM
-#include <io.h>
-#include <fcntl.h>
-#endif
-
-#include "util.h"
-#include "cipher.h"
-#include "i18n.h"
-
-static void
-my_usage(void)
-{
- fprintf(stderr, "usage: bftest [-e][-d] algo key\n");
- exit(1);
-}
-
-const char *
-strusage( int level )
-{
- return default_strusage(level);
-}
-
-static void
-i18n_init(void)
-{
-#ifdef ENABLE_NLS
- setlocale( LC_ALL, "" );
- bindtextdomain( PACKAGE, G10_LOCALEDIR );
- textdomain( PACKAGE );
-#endif
-}
-
-int
-main(int argc, char **argv)
-{
- int encode=0;
- CIPHER_HANDLE hd;
- char buf[4096];
- int n, size=4096;
- int algo;
-
-#ifdef HAVE_DOSISH_SYSTEM
- setmode( fileno(stdin), O_BINARY );
- setmode( fileno(stdout), O_BINARY );
-#endif
-
- i18n_init();
- if( argc > 1 && !strcmp(argv[1], "-e") ) {
- encode++;
- argc--; argv++;
- }
- else if( argc > 1 && !strcmp(argv[1], "-E") ) {
- encode++;
- argc--; argv++;
- size = 10;
- }
- else if( argc > 1 && !strcmp(argv[1], "-d") ) {
- argc--; argv++;
- }
- else if( argc > 1 && !strcmp(argv[1], "-D") ) {
- argc--; argv++;
- size = 10;
- }
- if( argc != 3 )
- my_usage();
- argc--; argv++;
- algo = string_to_cipher_algo( *argv );
- argc--; argv++;
-
- hd = cipher_open( algo, CIPHER_MODE_CFB, 0 );
- cipher_setkey( hd, *argv, strlen(*argv) );
- cipher_setiv( hd, NULL, 0 );
- while( (n = fread( buf, 1, size, stdin )) > 0 ) {
- if( encode )
- cipher_encrypt( hd, buf, buf, n );
- else
- cipher_decrypt( hd, buf, buf, n );
- if( fwrite( buf, 1, n, stdout) != n )
- log_fatal("write error\n");
- }
- cipher_close(hd);
- return 0;
-}
diff --git a/tools/clean-sat.c b/tools/clean-sat.c
deleted file mode 100644
index 8b6bfd77a..000000000
--- a/tools/clean-sat.c
+++ /dev/null
@@ -1,34 +0,0 @@
-/* clean-sat.c
- * Copyright (C) 1998, 1999, 2000, 2001 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 program 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.
- */
-
-#include <stdio.h>
-
-int
-main(int argc, char **argv)
-{
- int c;
-
- if( argc > 1 ) {
- fprintf(stderr, "no arguments, please\n");
- return 1;
- }
-
- while( (c=getchar()) == '\n' )
- ;
- while( c != EOF ) {
- putchar(c);
- c = getchar();
- }
-
- return 0;
-}
-
diff --git a/tools/convert-from-106 b/tools/convert-from-106
deleted file mode 100755
index 634152b11..000000000
--- a/tools/convert-from-106
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2002, 2004 Free Software Foundation, Inc.
-#
-# This program 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.
-#
-# This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-if ! gpg --version > /dev/null 2>&1 ; then
- echo "GnuPG not available!"
- exit 1
-fi
-
-gpg="gpg --no-greeting --no-secmem-warning"
-
-echo "This script converts your public keyring and trustdb from GnuPG"
-echo "1.0.6 or earlier to the 1.0.7 and later format."
-
-echo "If you have already done this, there is no harm (but no point)"
-echo "in doing it again."
-
-echo -n "Continue? (y/N)"
-
-read answer
-
-if test "x$answer" != "xy" ; then
- exit 0
-fi
-
-echo
-echo "Marking your keys as ultimately trusted"
-for key in `$gpg --with-colons --list-secret-keys | grep sec: | cut -d: -f5`
-do
- $gpg --trusted-key $key --with-colons --list-keys $key > /dev/null 2>&1
- echo -n "."
-done
-echo
-
-echo
-echo "Adding signature caches"
-$gpg --rebuild-keydb-caches
-
-echo
-echo "Checking trustdb"
-$gpg --check-trustdb
-
-echo
-echo "Done!"
diff --git a/tools/crlf.c b/tools/crlf.c
deleted file mode 100644
index ecb6eecdb..000000000
--- a/tools/crlf.c
+++ /dev/null
@@ -1,52 +0,0 @@
-/* crlf.c
- * Copyright (C) 1999, 2000, 2001 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 program 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.
- */
-
-#include <stdio.h>
-
-int
-main(int argc, char **argv)
-{
- int c, lc;
- int off=0;
-
- if( argc > 1 ) {
- fprintf(stderr, "no arguments, please\n");
- return 1;
- }
-
- lc = -1;
- while( (c=getchar()) != EOF ) {
-#if 0
- if( c == '\r' && lc == ' ' )
- fprintf(stderr,"SP,CR at %d\n", off );
- if( c == '\n' && lc == ' ' )
- fprintf(stderr,"SP,LF at %d\n", off );
-#endif
- if( c == '\n' && lc == '\r' )
- putchar(c);
- else if( c == '\n' ) {
- putchar('\r');
- putchar(c);
- }
- else if( c != '\n' && lc == '\r' ) {
- putchar('\n');
- putchar(c);
- }
- else
- putchar(c);
-
- lc = c;
- off++;
- }
-
- return 0;
-}
diff --git a/tools/der-to-pem b/tools/der-to-pem
new file mode 100755
index 000000000..183996654
--- /dev/null
+++ b/tools/der-to-pem
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Convert A BER or DER encoding to PEM format.
+#
+# Copyright 20032 Free Software Foundation, Inc.
+#
+# This program 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 program 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.
+
+PGM="der-to-pem"
+if [ $# == 0 ]; then
+ input=""
+elif [ $# == 1 ]; then
+ input="$1"
+else
+ echo "usage: $PGM [<inputfile>]" >&2
+ exit 1
+fi
+
+echo "-----BEGIN CERTIFICATE-----"
+mimencode $input
+echo "-----END CERTIFICATE-----"
+
diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c
new file mode 100644
index 000000000..90e321000
--- /dev/null
+++ b/tools/gpg-connect-agent.c
@@ -0,0 +1,639 @@
+/* gpg-connect-agent.c - Tool to connect to the agent.
+ * 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>
+#include <errno.h>
+#include <ctype.h>
+#include <assuan.h>
+
+#include "i18n.h"
+#include "../common/util.h"
+#include "../common/asshelp.h"
+
+
+
+/* Constants to identify the commands and options. */
+enum cmd_and_opt_values
+ {
+ aNull = 0,
+ oQuiet = 'q',
+ oVerbose = 'v',
+ oRawSocket = 'S',
+
+ oNoVerbose = 500,
+ oHomedir,
+ oHex
+
+ };
+
+
+/* The list of commands and options. */
+static ARGPARSE_OPTS opts[] =
+ {
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("quiet") },
+ { oHex, "hex", 0, N_("print data out hex encoded") },
+ { oRawSocket, "raw-socket", 2, N_("|NAME|connect to Assuan socket NAME")},
+
+ /* hidden options */
+ { oNoVerbose, "no-verbose", 0, "@"},
+ { oHomedir, "homedir", 2, "@" },
+ {0}
+ };
+
+
+/* We keep all global options in the structure OPT. */
+struct
+{
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be extra quiet. */
+ const char *homedir; /* Configuration directory name */
+ int hex; /* Print data lines in hex format. */
+ const char *raw_socket; /* Name of socket to connect in raw mode. */
+} opt;
+
+
+
+/* Definitions for /definq commands and a global linked list with all
+ the definitions. */
+struct definq_s
+{
+ struct definq_s *next;
+ char *name; /* Name of inquiry or NULL for any name. */
+ int is_prog; /* True if this is a program to run. */
+ char file[1]; /* Name of file or program. */
+};
+typedef struct definq_s *definq_t;
+
+static definq_t definq_list;
+static definq_t *definq_list_tail = &definq_list;
+
+
+
+/*-- local prototypes --*/
+static int read_and_print_response (assuan_context_t ctx);
+static assuan_context_t start_agent (void);
+
+
+
+
+/* Print usage information and and provide strings for help. */
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 11: p = "gpg-connect-agent (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: gpg-connect-agent [options] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: gpg-connect-agent [options]\n"
+ "Connect to a running agent and send commands\n");
+ break;
+ case 31: p = "\nHome: "; break;
+ case 32: p = opt.homedir; break;
+ case 33: p = "\n"; break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+/* Initialize the gettext system. */
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE_GT);
+#else
+# ifdef ENABLE_NLS
+ setlocale (LC_ALL, "" );
+ bindtextdomain (PACKAGE_GT, LOCALEDIR);
+ textdomain (PACKAGE_GT);
+# endif
+#endif
+}
+
+/* Store an inquire response pattern. Note, that this function may
+ change the content of LINE. We assume that leading white spaces
+ are already removed. */
+static void
+add_definq (char *line, int is_prog)
+{
+ definq_t d;
+ char *name, *p;
+
+ /* Get name. */
+ name = line;
+ for (p=name; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ *p++ = 0;
+ while (spacep (p))
+ p++;
+
+ d = xmalloc (sizeof *d + strlen (p) );
+ strcpy (d->file, p);
+ d->is_prog = is_prog;
+ if ( !strcmp (name, "*"))
+ d->name = NULL;
+ else
+ d->name = xstrdup (name);
+
+ d->next = NULL;
+ *definq_list_tail = d;
+ definq_list_tail = &d->next;
+}
+
+
+/* Show all inquiry defintions. */
+static void
+show_definq (void)
+{
+ definq_t d;
+
+ for (d=definq_list; d; d = d->next)
+ if (d->name)
+ printf ("%-20s %c %s\n", d->name, d->is_prog? 'p':'f', d->file);
+ for (d=definq_list; d; d = d->next)
+ if (!d->name)
+ printf ("%-20s %c %s\n", "*", d->is_prog? 'p':'f', d->file);
+}
+
+
+/* Clear all inquiry definitions. */
+static void
+clear_definq (void)
+{
+ while (definq_list)
+ {
+ definq_t tmp = definq_list->next;
+ xfree (definq_list->name);
+ xfree (definq_list);
+ definq_list = tmp;
+ }
+ definq_list_tail = &definq_list;
+}
+
+
+
+/* gpg-connect-agent's entry point. */
+int
+main (int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ const char *fname;
+ int no_more_options = 0;
+ assuan_context_t ctx;
+ char *line, *p;
+ size_t linesize;
+ int rc;
+
+ set_strusage (my_strusage);
+ log_set_prefix ("gpg-connect-agent", 1);
+
+ i18n_init();
+
+ opt.homedir = default_homedir ();
+
+ /* Parse the command line. */
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags = 1; /* Do not remove the args. */
+ while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: opt.verbose++; break;
+ case oNoVerbose: opt.verbose = 0; break;
+ case oHomedir: opt.homedir = pargs.r.ret_str; break;
+ case oHex: opt.hex = 1; break;
+ case oRawSocket: opt.raw_socket = pargs.r.ret_str; break;
+
+ default: pargs.err = 2; break;
+ }
+ }
+
+ if (log_get_errorcount (0))
+ exit (2);
+
+ fname = argc ? *argv : NULL;
+
+ if (opt.raw_socket)
+ {
+ rc = assuan_socket_connect (&ctx, opt.raw_socket, 0);
+ if (rc)
+ {
+ log_error ("can't connect to socket `%s': %s\n",
+ opt.raw_socket, assuan_strerror (rc));
+ exit (1);
+ }
+
+ if (opt.verbose)
+ log_info ("connection to socket `%s' established\n", opt.raw_socket);
+ }
+ else
+ ctx = start_agent ();
+ line = NULL;
+ linesize = 0;
+ for (;;)
+ {
+ int n;
+ size_t maxlength;
+
+ maxlength = 2048;
+ n = read_line (stdin, &line, &linesize, &maxlength);
+ if (n < 0)
+ {
+ log_error (_("error reading input: %s\n"), strerror (errno));
+ exit (1);
+ }
+ if (!n)
+ break; /* EOF */
+ if (!maxlength)
+ {
+ log_error (_("line too long - skipped\n"));
+ continue;
+ }
+ if (memchr (line, 0, n))
+ log_info (_("line shortened due to embedded Nul character\n"));
+ if (line[n-1] == '\n')
+ line[n-1] = 0;
+ if (*line == '/')
+ {
+ /* Handle control commands. */
+ char *cmd = line+1;
+
+ for (p=cmd; *p && !spacep (p); p++)
+ ;
+ if (*p)
+ *p++ = 0;
+ while (spacep (p))
+ p++;
+ if (!strcmp (cmd, "definqfile"))
+ {
+ add_definq (p, 0);
+ }
+ else if (!strcmp (cmd, "definqprog"))
+ {
+ add_definq (p, 1);
+ }
+ else if (!strcmp (cmd, "showdef"))
+ {
+ show_definq ();
+ }
+ else if (!strcmp (cmd, "cleardef"))
+ {
+ clear_definq ();
+ }
+ else if (!strcmp (cmd, "echo"))
+ {
+ puts (p);
+ }
+ else if (!strcmp (cmd, "help"))
+ {
+ puts ("Available commands:\n"
+ "/echo ARGS Echo ARGS.\n"
+ "/definqfile NAME FILE\n"
+ " Use content of FILE for inquiries with NAME.\n"
+ " NAME may be \"*\" to match any inquiry.\n"
+ "/definqprog NAME PGM\n"
+ " Run PGM for inquiries matching NAME and pass the\n"
+ " entire line to it as arguments.\n"
+ "/showdef Print all definitions.\n"
+ "/cleardef Delete all definitions.\n"
+ "/help Print this help.");
+ }
+ else
+ log_error (_("unknown command `%s'\n"), cmd );
+
+ continue;
+ }
+
+ rc = assuan_write_line (ctx, line);
+ if (rc)
+ {
+ log_info (_("sending line failed: %s\n"), assuan_strerror (rc) );
+ continue;
+ }
+ if (*line == '#' || !*line)
+ continue; /* Don't expect a response for a coment line. */
+
+ rc = read_and_print_response (ctx);
+ if (rc)
+ log_info (_("receiving line failed: %s\n"), assuan_strerror (rc) );
+ }
+
+ if (opt.verbose)
+ log_info ("closing connection to agent\n");
+
+ return 0;
+}
+
+
+/* Handle an Inquire from the server. Return False if it could not be
+ handled; in this case the caller shll complete the operation. LINE
+ is the complete line as received from the server. This function
+ may change the content of LINE. */
+static int
+handle_inquire (assuan_context_t ctx, char *line)
+{
+ const char *name;
+ definq_t d;
+ FILE *fp;
+ char buffer[1024];
+ int rc, n;
+
+ /* Skip the command and trailing spaces. */
+ for (; *line && !spacep (line); line++)
+ ;
+ while (spacep (line))
+ line++;
+ /* Get the name. */
+ name = line;
+ for (; *line && !spacep (line); line++)
+ ;
+ if (*line)
+ *line++ = 0;
+
+ /* Now match it against our list. he second loop is todetect the
+ match all entry. **/
+ for (d=definq_list; d; d = d->next)
+ if (d->name && !strcmp (d->name, name))
+ break;
+ if (!d)
+ for (d=definq_list; d; d = d->next)
+ if (!d->name)
+ break;
+ if (!d)
+ {
+ if (opt.verbose)
+ log_info ("no handler for inquiry `%s' found\n", name);
+ return 0;
+ }
+
+ if (d->is_prog)
+ {
+ fp = popen (d->file, "r");
+ if (!fp)
+ log_error ("error executing `%s': %s\n", d->file, strerror (errno));
+ else if (opt.verbose)
+ log_error ("handling inquiry `%s' by running `%s'\n", name, d->file);
+ }
+ else
+ {
+ fp = fopen (d->file, "rb");
+ if (!fp)
+ log_error ("error opening `%s': %s\n", d->file, strerror (errno));
+ else if (opt.verbose)
+ log_error ("handling inquiry `%s' by returning content of `%s'\n",
+ name, d->file);
+ }
+ if (!fp)
+ return 0;
+
+ while ( (n = fread (buffer, 1, sizeof buffer, fp)) )
+ {
+ rc = assuan_send_data (ctx, buffer, n);
+ if (rc)
+ {
+ log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
+ break;
+ }
+ }
+ if (ferror (fp))
+ log_error ("error reading from `%s': %s\n", d->file, strerror (errno));
+
+ rc = assuan_send_data (ctx, NULL, 0);
+ if (rc)
+ log_error ("sending data back failed: %s\n", assuan_strerror (rc) );
+
+ if (d->is_prog)
+ {
+ if (pclose (fp))
+ log_error ("error running `%s': %s\n", d->file, strerror (errno));
+ }
+ else
+ fclose (fp);
+ return 1;
+}
+
+
+/* Read all response lines from server and print them. Returns 0 on
+ success or an assuan error code. */
+static int
+read_and_print_response (assuan_context_t ctx)
+{
+ char *line;
+ size_t linelen;
+ assuan_error_t rc;
+ int i, j;
+
+ for (;;)
+ {
+ do
+ {
+ rc = assuan_read_line (ctx, &line, &linelen);
+ if (rc)
+ return rc;
+ }
+ while (*line == '#' || !linelen);
+
+ if (linelen >= 1
+ && line[0] == 'D' && line[1] == ' ')
+ {
+ if (opt.hex)
+ {
+ for (i=2; i < linelen; )
+ {
+ int save_i = i;
+
+ printf ("D[%04X] ", i-2);
+ for (j=0; j < 16 ; j++, i++)
+ {
+ if (j == 8)
+ putchar (' ');
+ if (i < linelen)
+ printf (" %02X", ((unsigned char*)line)[i]);
+ else
+ fputs (" ", stdout);
+ }
+ fputs (" ", stdout);
+ i= save_i;
+ for (j=0; j < 16; j++, i++)
+ {
+ unsigned int c = ((unsigned char*)line)[i];
+ if ( i >= linelen )
+ putchar (' ');
+ else if (isascii (c) && isprint (c) && !iscntrl (c))
+ putchar (c);
+ else
+ putchar ('.');
+ }
+ putchar ('\n');
+ }
+ }
+ else
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ }
+ }
+ else if (linelen >= 1
+ && line[0] == 'S'
+ && (line[1] == '\0' || line[1] == ' '))
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ }
+ else if (linelen >= 2
+ && line[0] == 'O' && line[1] == 'K'
+ && (line[2] == '\0' || line[2] == ' '))
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ return 0;
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'R' && line[2] == 'R'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ return 0;
+ }
+ else if (linelen >= 7
+ && line[0] == 'I' && line[1] == 'N' && line[2] == 'Q'
+ && line[3] == 'U' && line[4] == 'I' && line[5] == 'R'
+ && line[6] == 'E'
+ && (line[7] == '\0' || line[7] == ' '))
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ if (!handle_inquire (ctx, line))
+ assuan_write_line (ctx, "CANCEL");
+ }
+ else if (linelen >= 3
+ && line[0] == 'E' && line[1] == 'N' && line[2] == 'D'
+ && (line[3] == '\0' || line[3] == ' '))
+ {
+ fwrite (line, linelen, 1, stdout);
+ putchar ('\n');
+ /* Received from server, thus more responses are expected. */
+ }
+ else
+ return ASSUAN_Invalid_Response;
+ }
+}
+
+
+
+
+/* Connect to the agent and send the standard options. */
+static assuan_context_t
+start_agent (void)
+{
+ int rc = 0;
+ char *infostr, *p;
+ assuan_context_t ctx;
+
+ infostr = getenv ("GPG_AGENT_INFO");
+ if (!infostr || !*infostr)
+ {
+ char *sockname;
+
+ /* Check whether we can connect at the standard socket. */
+ sockname = make_filename (opt.homedir, "S.gpg-agent", NULL);
+ rc = assuan_socket_connect (&ctx, sockname, 0);
+ xfree (sockname);
+ }
+ else
+ {
+ int prot;
+ int pid;
+
+ infostr = xstrdup (infostr);
+ if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
+ {
+ log_error (_("malformed GPG_AGENT_INFO environment variable\n"));
+ xfree (infostr);
+ exit (1);
+ }
+ *p++ = 0;
+ pid = atoi (p);
+ while (*p && *p != PATHSEP_C)
+ p++;
+ prot = *p? atoi (p+1) : 0;
+ if (prot != 1)
+ {
+ log_error (_("gpg-agent protocol version %d is not supported\n"),
+ prot);
+ xfree (infostr);
+ exit (1);
+ }
+
+ rc = assuan_socket_connect (&ctx, infostr, pid);
+ xfree (infostr);
+ }
+
+ if (rc)
+ {
+ log_error ("can't connect to the agent: %s\n", assuan_strerror (rc));
+ exit (1);
+ }
+
+ if (opt.verbose)
+ log_info ("connection to agent established\n");
+
+ rc = assuan_transact (ctx, "RESET", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ {
+ log_error (_("error sending %s command: %s\n"), "RESET",
+ assuan_strerror (rc));
+ exit (1);
+ }
+
+ rc = send_pinentry_environment (ctx, GPG_ERR_SOURCE_DEFAULT,
+ NULL, NULL, NULL, NULL, NULL);
+ if (rc)
+ {
+ log_error (_("error sending standard options: %s\n"), gpg_strerror (rc));
+ exit (1);
+ }
+
+ return ctx;
+}
diff --git a/tools/gpg-zip.in b/tools/gpg-zip.in
deleted file mode 100644
index 8b4ccfb2b..000000000
--- a/tools/gpg-zip.in
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/sh
-
-# gpg-archive - gpg-ized tar using the same format as PGP's PGP Zip.
-# 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
-
-# Despite the name, PGP Zip format is actually an OpenPGP-wrapped tar
-# file. To be compatible with PGP itself, this must be a USTAR format
-# tar file. Unclear on whether there is a distinction here between
-# the GNU or POSIX variant of USTAR.
-
-VERSION=@VERSION@
-TAR=@TAR@
-GPG=gpg
-
-usage="\
-Usage: gpg-zip [--help] [--version] [--encrypt] [--decrypt] [--symmetric]
- [--list-archive] [--output FILE] [--gpg GPG] [--gpg-args ARGS]
- [--tar TAR] [--tar-args ARGS] filename1 [filename2, ...]
- directory1 [directory2, ...]
-
-Encrypt or sign files into an archive."
-
-while test $# -gt 0 ; do
- case $1 in
- -h | --help | --h*)
- echo "$usage"
- exit 0
- ;;
- --list-archive)
- list=yes
- create=no
- unpack=no
- shift
- ;;
- --encrypt | -e)
- gpg_args="$gpg_args --encrypt"
- list=no
- create=yes
- unpack=no
- shift
- ;;
- --decrypt | -d)
- gpg_args="$gpg_args --decrypt"
- list=no
- create=no
- unpack=yes
- shift
- ;;
- --symmetric | -c)
- gpg_args="$gpg_args --symmetric"
- list=no
- create=yes
- unpack=no
- shift
- ;;
- --sign | -s)
- gpg_args="$gpg_args --sign"
- list=no
- create=yes
- unpack=no
- shift
- ;;
- --recipient | -r)
- gpg_args="$gpg_args --recipient $2"
- shift
- shift
- ;;
- --local-user | -u)
- gpg_args="$gpg_args --local-user $2"
- shift
- shift
- ;;
- --output | -o)
- gpg_args="$gpg_args --output $2"
- shift
- shift
- ;;
- --version)
- echo "gpg-zip (GnuPG) $VERSION"
- exit 0
- ;;
- --gpg)
- GPG=$1
- shift
- ;;
- --gpg-args)
- gpg_args="$gpg_args $2"
- shift
- shift
- ;;
- --tar)
- TAR=$1
- shift
- ;;
- --tar-args)
- tar_args="$tar_args $2"
- shift
- shift
- ;;
- --)
- shift
- break
- ;;
- -*)
- echo "$usage" 1>&2
- exit 1
- ;;
- *)
- break
- ;;
- esac
-done
-
-if test x$create = xyes ; then
-# echo "$TAR -cf - "$@" | $GPG --set-filename x.tar $gpg_args" 1>&2
- $TAR -cf - "$@" | $GPG --set-filename x.tar $gpg_args
-elif test x$list = xyes ; then
-# echo "cat \"$1\" | $GPG $gpg_args | $TAR $tar_args -tf -" 1>&2
- cat "$1" | $GPG $gpg_args | $TAR $tar_args -tf -
-elif test x$unpack = xyes ; then
-# echo "cat \"$1\" | $GPG $gpg_args | $TAR $tar_args -xvf -" 1>&2
- cat "$1" | $GPG $gpg_args | $TAR $tar_args -xvf -
-else
- echo "$usage" 1>&2
- exit 1
-fi
diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c
new file mode 100644
index 000000000..04a61a193
--- /dev/null
+++ b/tools/gpgconf-comp.c
@@ -0,0 +1,2499 @@
+/* gpgconf-comp.c - Configuration utility for GnuPG.
+ * 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 GnuPG; if not, write to the Free Software Foundation,
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <time.h>
+#include <stdarg.h>
+#include <signal.h>
+
+/* For log_logv(), asctimestamp(), gnupg_get_time (). */
+#define JNLIB_NEED_LOG_LOGV
+#include "util.h"
+#include "i18n.h"
+
+#include "gpgconf.h"
+
+
+
+/* TODO:
+ Components: Add more components and their options.
+ Robustness: Do more validation. Call programs to do validation for us.
+ Don't use popen, as this will not tell us if the program had a
+ non-zero exit code.
+ Add options to change backend binary path.
+ Extract binary path for some backends from gpgsm/gpg config.
+*/
+
+
+#if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ))
+void gc_error (int status, int errnum, const char *fmt, ...) \
+ __attribute__ ((format (printf, 3, 4)));
+#endif
+
+/* Output a diagnostic message. If ERRNUM is not 0, then the output
+ is followed by a colon, a white space, and the error string for the
+ error number ERRNUM. In any case the output is finished by a
+ newline. The message is prepended by the program name, a colon,
+ and a whitespace. The output may be further formatted or
+ redirected by the jnlib logging facility. */
+void
+gc_error (int status, int errnum, const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ log_logv (JNLIB_LOG_ERROR, fmt, arg_ptr);
+ va_end (arg_ptr);
+
+ if (errnum)
+ log_printf (": %s\n", strerror (errnum));
+ else
+ log_printf ("\n");
+
+ if (status)
+ {
+ log_printf (NULL);
+ log_printf ("fatal error (exit status %i)\n", status);
+ exit (status);
+ }
+}
+
+
+/* Forward declaration. */
+void gpg_agent_runtime_change (void);
+
+/* Backend configuration. Backends are used to decide how the default
+ and current value of an option can be determined, and how the
+ option can be changed. To every option in every component belongs
+ exactly one backend that controls and determines the option. Some
+ backends are programs from the GPG system. Others might be
+ implemented by GPGConf itself. If you change this enum, don't
+ forget to update GC_BACKEND below. */
+typedef enum
+ {
+ /* Any backend, used for find_option (). */
+ GC_BACKEND_ANY,
+
+ /* The Gnu Privacy Guard. */
+ GC_BACKEND_GPG,
+
+ /* The Gnu Privacy Guard for S/MIME. */
+ GC_BACKEND_GPGSM,
+
+ /* The GPG Agent. */
+ GC_BACKEND_GPG_AGENT,
+
+ /* The GnuPG SCDaemon. */
+ GC_BACKEND_SCDAEMON,
+
+ /* The Aegypten directory manager. */
+ GC_BACKEND_DIRMNGR,
+
+ /* The LDAP server list file for the Aegypten director manager. */
+ GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST,
+
+ /* The number of the above entries. */
+ GC_BACKEND_NR
+ } gc_backend_t;
+
+
+/* To be able to implement generic algorithms for the various
+ backends, we collect all information about them in this struct. */
+static struct
+{
+ /* The name of the backend. */
+ const char *name;
+
+ /* The name of the program that acts as the backend. Some backends
+ don't have an associated program, but are implemented directly by
+ GPGConf. In this case, PROGRAM is NULL. */
+ char *program;
+
+ /* The runtime change callback. */
+ void (*runtime_change) (void);
+
+ /* The option name for the configuration filename of this backend.
+ This must be an absolute pathname. It can be an option from a
+ different backend (but then ordering of the options might
+ matter). */
+ const char *option_config_filename;
+
+ /* If this is a file backend rather than a program backend, then
+ this is the name of the option associated with the file. */
+ const char *option_name;
+} gc_backend[GC_BACKEND_NR] =
+ {
+ { NULL }, /* GC_BACKEND_ANY dummy entry. */
+ { "GnuPG", "gpg", NULL, "gpgconf-gpg.conf" },
+ { "GPGSM", "gpgsm", NULL, "gpgconf-gpgsm.conf" },
+ { "GPG Agent", "gpg-agent", gpg_agent_runtime_change,
+ "gpgconf-gpg-agent.conf" },
+ { "SCDaemon", "scdaemon", NULL, "gpgconf-scdaemon.conf" },
+ { "DirMngr", "dirmngr", NULL, "gpgconf-dirmngr.conf" },
+ { "DirMngr LDAP Server List", NULL, NULL, "ldapserverlist-file",
+ "LDAP Server" },
+ };
+
+
+/* Option configuration. */
+
+/* An option might take an argument, or not. Argument types can be
+ basic or complex. Basic types are generic and easy to validate.
+ Complex types provide more specific information about the intended
+ use, but can be difficult to validate. If you add to this enum,
+ don't forget to update GC_ARG_TYPE below. YOU MUST NOT CHANGE THE
+ NUMBERS OF THE EXISTING ENTRIES, AS THEY ARE PART OF THE EXTERNAL
+ INTERFACE. */
+typedef enum
+ {
+ /* Basic argument types. */
+
+ /* No argument. */
+ GC_ARG_TYPE_NONE = 0,
+
+ /* A String argument. */
+ GC_ARG_TYPE_STRING = 1,
+
+ /* A signed integer argument. */
+ GC_ARG_TYPE_INT32 = 2,
+
+ /* An unsigned integer argument. */
+ GC_ARG_TYPE_UINT32 = 3,
+
+ /* ADD NEW BASIC TYPE ENTRIES HERE. */
+
+ /* Complex argument types. */
+
+ /* A complete pathname. */
+ GC_ARG_TYPE_PATHNAME = 32,
+
+ /* An LDAP server in the format
+ HOSTNAME:PORT:USERNAME:PASSWORD:BASE_DN. */
+ GC_ARG_TYPE_LDAP_SERVER = 33,
+
+ /* A 40 character fingerprint. */
+ GC_ARG_TYPE_KEY_FPR = 34,
+
+ /* ADD NEW COMPLEX TYPE ENTRIES HERE. */
+
+ /* The number of the above entries. */
+ GC_ARG_TYPE_NR
+ } gc_arg_type_t;
+
+
+/* For every argument, we record some information about it in the
+ following struct. */
+static struct
+{
+ /* For every argument type exists a basic argument type that can be
+ used as a fallback for input and validation purposes. */
+ gc_arg_type_t fallback;
+
+ /* Human-readable name of the type. */
+ const char *name;
+} gc_arg_type[GC_ARG_TYPE_NR] =
+ {
+ /* The basic argument types have their own types as fallback. */
+ { GC_ARG_TYPE_NONE, "none" },
+ { GC_ARG_TYPE_STRING, "string" },
+ { GC_ARG_TYPE_INT32, "int32" },
+ { GC_ARG_TYPE_UINT32, "uint32" },
+
+ /* Reserved basic type entries for future extension. */
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+ { GC_ARG_TYPE_NR, NULL }, { GC_ARG_TYPE_NR, NULL },
+
+ /* The complex argument types have a basic type as fallback. */
+ { GC_ARG_TYPE_STRING, "pathname" },
+ { GC_ARG_TYPE_STRING, "ldap server" },
+ { GC_ARG_TYPE_STRING, "key fpr" },
+ };
+
+
+/* Every option has an associated expert level, than can be used to
+ hide advanced and expert options from beginners. If you add to
+ this list, don't forget to update GC_LEVEL below. YOU MUST NOT
+ CHANGE THE NUMBERS OF THE EXISTING ENTRIES, AS THEY ARE PART OF THE
+ EXTERNAL INTERFACE. */
+typedef enum
+ {
+ /* The basic options should always be displayed. */
+ GC_LEVEL_BASIC,
+
+ /* The advanced options may be hidden from beginners. */
+ GC_LEVEL_ADVANCED,
+
+ /* The expert options should only be displayed to experts. */
+ GC_LEVEL_EXPERT,
+
+ /* The invisible options should normally never be displayed. */
+ GC_LEVEL_INVISIBLE,
+
+ /* The internal options are never exported, they mark options that
+ are recorded for internal use only. */
+ GC_LEVEL_INTERNAL,
+
+ /* ADD NEW ENTRIES HERE. */
+
+ /* The number of the above entries. */
+ GC_LEVEL_NR
+ } gc_expert_level_t;
+
+/* A description for each expert level. */
+static struct
+{
+ const char *name;
+} gc_level[] =
+ {
+ { "basic" },
+ { "advanced" },
+ { "expert" },
+ { "invisible" },
+ { "internal" }
+ };
+
+
+/* Option flags. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING
+ FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */
+#define GC_OPT_FLAG_NONE 0UL
+/* Some entries in the option list are not options, but mark the
+ beginning of a new group of options. These entries have the GROUP
+ flag set. */
+#define GC_OPT_FLAG_GROUP (1UL << 0)
+/* The ARG_OPT flag for an option indicates that the argument is
+ optional. This is never set for GC_ARG_TYPE_NONE options. */
+#define GC_OPT_FLAG_ARG_OPT (1UL << 1)
+/* The LIST flag for an option indicates that the option can occur
+ several times. A comma separated list of arguments is used as the
+ argument value. */
+#define GC_OPT_FLAG_LIST (1UL << 2)
+/* The RUNTIME flag for an option indicates that the option can be
+ changed at runtime. */
+#define GC_OPT_FLAG_RUNTIME (1UL << 3)
+
+/* The following flags are incorporated from the backend. */
+/* The DEFAULT flag for an option indicates that the option has a
+ default value. */
+#define GC_OPT_FLAG_DEFAULT (1UL << 4)
+/* The DEF_DESC flag for an option indicates that the option has a
+ default, which is described by the value of the default field. */
+#define GC_OPT_FLAG_DEF_DESC (1UL << 5)
+/* The NO_ARG_DESC flag for an option indicates that the argument has
+ a default, which is described by the value of the ARGDEF field. */
+#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6)
+
+/* A human-readable description for each flag. */
+static struct
+{
+ const char *name;
+} gc_flag[] =
+ {
+ { "group" },
+ { "optional arg" },
+ { "list" },
+ { "runtime" },
+ { "default" },
+ { "default desc" },
+ { "no arg desc" }
+ };
+
+
+/* To each option, or group marker, the information in the GC_OPTION
+ struct is provided. If you change this, don't forget to update the
+ option list of each component. */
+struct gc_option
+{
+ /* If this is NULL, then this is a terminator in an array of unknown
+ length. Otherwise, if this entry is a group marker (see FLAGS),
+ then this is the name of the group described by this entry.
+ Otherwise it is the name of the option described by this
+ entry. The name must not contain a colon. */
+ const char *name;
+
+ /* The option flags. If the GROUP flag is set, then this entry is a
+ group marker, not an option, and only the fields LEVEL,
+ DESC_DOMAIN and DESC are valid. In all other cases, this entry
+ describes a new option and all fields are valid. */
+ unsigned long flags;
+
+ /* The expert level. This field is valid for options and groups. A
+ group has the expert level of the lowest-level option in the
+ group. */
+ gc_expert_level_t level;
+
+ /* A gettext domain in which the following description can be found.
+ If this is NULL, then DESC is not translated. Valid for groups
+ and options.
+
+ Note that we try to keep the description of groups within the
+ gnupg domain.
+
+ IMPORTANT: If you add a new domain please make sure to add a code
+ set switching call to the function my_dgettext further below. */
+ const char *desc_domain;
+
+ /* A gettext description for this group or option. If it starts
+ with a '|', then the string up to the next '|' describes the
+ argument, and the description follows the second '|'.
+
+ In general enclosing these description in N_() is not required
+ because the description should be identical to the one in the
+ help menu of the respective program. */
+ const char *desc;
+
+ /* The following fields are only valid for options. */
+
+ /* The type of the option argument. */
+ gc_arg_type_t arg_type;
+
+ /* The backend that implements this option. */
+ gc_backend_t backend;
+
+ /* The following fields are set to NULL at startup (because all
+ option's are declared as static variables). They are at the end
+ of the list so that they can be omitted from the option
+ declarations. */
+
+ /* This is true if the option is supported by this version of the
+ backend. */
+ int active;
+
+ /* The default value for this option. This is NULL if the option is
+ not present in the backend, the empty string if no default is
+ available, and otherwise a quoted string. */
+ char *default_value;
+
+ /* The default argument is only valid if the "optional arg" flag is
+ set, and specifies the default argument (value) that is used if
+ the argument is omitted. */
+ char *default_arg;
+
+ /* The current value of this option. */
+ char *value;
+
+ /* The new flags for this option. The only defined flag is actually
+ GC_OPT_FLAG_DEFAULT, and it means that the option should be
+ deleted. In this case, NEW_VALUE is NULL. */
+ unsigned long new_flags;
+
+ /* The new value of this option. */
+ char *new_value;
+};
+typedef struct gc_option gc_option_t;
+
+/* Use this macro to terminate an option list. */
+#define GC_OPTION_NULL { NULL }
+
+
+/* The options of the GC_COMPONENT_GPG_AGENT component. */
+static gc_option_t gc_options_gpg_agent[] =
+ {
+ /* The configuration file to which we write the changes. */
+ { "gpgconf-gpg-agent.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT },
+
+ { "Monitor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the diagnostic output") },
+ { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
+ "gnupg", "verbose",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "quiet", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
+ "gnupg", "be somewhat more quiet",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+
+ { "Configuration",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT,
+ "gnupg", N_("Options controlling the configuration") },
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "|FILE|read options from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT },
+ { "disable-scdaemon", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "do not use the SCdaemon",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+
+ { "Debug",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Options useful for debugging") },
+ { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED,
+ "gnupg", "|LEVEL|set the debugging level to LEVEL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT },
+ { "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED,
+ "gnupg", N_("|FILE|write server mode logs to FILE"),
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG_AGENT },
+ { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
+
+ { "Security",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the security") },
+ { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
+ "gnupg", "|N|expire cached PINs after N seconds",
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
+ { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
+ "gnupg", "do not use the PIN cache when signing",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED,
+ "gnupg", "allow clients to mark keys as \"trusted\"",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
+ "gnupg", "do not grab keyboard and mouse",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+
+
+ GC_OPTION_NULL
+ };
+
+
+/* The options of the GC_COMPONENT_SCDAEMON component. */
+static gc_option_t gc_options_scdaemon[] =
+ {
+ /* The configuration file to which we write the changes. */
+ { "gpgconf-scdaemon.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON },
+
+ { "Monitor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the diagnostic output") },
+ { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
+ "gnupg", "verbose",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+ { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "be somewhat more quiet",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+ { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+
+ { "Configuration",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT,
+ "gnupg", N_("Options controlling the configuration") },
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "|FILE|read options from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON },
+ { "reader-port", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "|N|connect to reader at port N",
+ GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON },
+ { "ctapi-driver", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "|NAME|use NAME as ct-API driver",
+ GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON },
+ { "pcsc-driver", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "|NAME|use NAME as PC/SC driver",
+ GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON },
+ { "disable-opensc", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "do not use the OpenSC layer",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+ { "disable-ccid", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "do not use the internal CCID driver",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+ { "disable-keypad", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "do not use a reader's keypad",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+
+ { "Debug",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Options useful for debugging") },
+ { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED,
+ "gnupg", "|LEVEL|set the debugging level to LEVEL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON },
+ { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", N_("|FILE|write server mode logs to FILE"),
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_SCDAEMON },
+
+ { "Security",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the security") },
+ { "allow-admin", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "allow the use of admin card commands",
+ GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON },
+
+
+ GC_OPTION_NULL
+ };
+
+
+/* The options of the GC_COMPONENT_GPG component. */
+static gc_option_t gc_options_gpg[] =
+ {
+ /* The configuration file to which we write the changes. */
+ { "gpgconf-gpg.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG },
+
+ { "Monitor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the diagnostic output") },
+ { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
+ "gnupg", "verbose",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+ { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "be somewhat more quiet",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+ { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+
+ { "Configuration",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT,
+ "gnupg", N_("Options controlling the configuration") },
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "|FILE|read options from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG },
+
+ { "Debug",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Options useful for debugging") },
+ { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED,
+ "gnupg", "|LEVEL|set the debugging level to LEVEL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", N_("|FILE|write server mode logs to FILE"),
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPG },
+/* { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, */
+/* NULL, NULL, */
+/* GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, */
+
+ { "Keyserver",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Configuration for Keyservers") },
+ { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "|URL|use keyserver at URL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
+ { "allow-pka-lookup", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", N_("allow PKA lookups (DNS requests)"),
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPG },
+
+
+ GC_OPTION_NULL
+ };
+
+
+
+/* The options of the GC_COMPONENT_GPGSM component. */
+static gc_option_t gc_options_gpgsm[] =
+ {
+ /* The configuration file to which we write the changes. */
+ { "gpgconf-gpgsm.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM },
+
+ { "Monitor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the diagnostic output") },
+ { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
+ "gnupg", "verbose",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "be somewhat more quiet",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+
+ { "Configuration",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT,
+ "gnupg", N_("Options controlling the configuration") },
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "|FILE|read options from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM },
+ { "prefer-system-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "use system's dirmngr if available",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+
+ { "Debug",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Options useful for debugging") },
+ { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED,
+ "gnupg", "|LEVEL|set the debugging level to LEVEL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM },
+ { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", N_("|FILE|write server mode logs to FILE"),
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_GPGSM },
+ { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPGSM },
+
+ { "Security",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the security") },
+ { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "never consult a CRL",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", N_("do not check CRLs for root certificates"),
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "enable-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "check validity using OCSP",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "include-certs", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "gnupg", "|N|number of certificates to include",
+ GC_ARG_TYPE_INT32, GC_BACKEND_GPGSM },
+ { "disable-policy-checks", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "gnupg", "do not check certificate policies",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+ { "auto-issuer-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "gnupg", "fetch missing issuer certificates",
+ GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
+
+ GC_OPTION_NULL
+ };
+
+
+/* The options of the GC_COMPONENT_DIRMNGR component. */
+static gc_option_t gc_options_dirmngr[] =
+ {
+ /* The configuration file to which we write the changes. */
+ { "gpgconf-dirmngr.conf", GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ NULL, NULL, GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR },
+
+ { "Monitor",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the diagnostic output") },
+ { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
+ "dirmngr", "verbose",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "be somewhat more quiet",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "Format",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the format of the output") },
+ { "sh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "sh-style command output",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "csh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "csh-style command output",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "Configuration",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT,
+ "gnupg", N_("Options controlling the configuration") },
+ { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
+ "dirmngr", "|FILE|read options from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR },
+
+ { "Debug",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Options useful for debugging") },
+ { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED,
+ "dirmngr", "|LEVEL|set the debugging level to LEVEL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
+ { "no-detach", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "do not detach from the console",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", N_("|FILE|write server mode logs to FILE"),
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR },
+ { "debug-wait", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR },
+ { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
+ NULL, NULL,
+ GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR },
+
+ { "Enforcement",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Options controlling the interactivity and enforcement") },
+ { "batch", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "run without asking a user",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "force loading of outdated CRLs",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "HTTP",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Configuration for HTTP servers") },
+ { "disable-http", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "inhibit the use of HTTP",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ignore-http-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "ignore HTTP CRL distribution points",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "|URL|redirect all HTTP requests to URL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
+ { "honor-http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", N_("use system's HTTP proxy setting"),
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+
+ { "LDAP",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
+ "gnupg", N_("Configuration of LDAP servers to use") },
+ { "disable-ldap", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "inhibit the use of LDAP",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ignore-ldap-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "ignore LDAP CRL distribution points",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "|HOST|use HOST for LDAP queries",
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
+ { "only-ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "do not use fallback hosts with --ldap-proxy",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "add-servers", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "add new servers discovered in CRL distribution points"
+ " to serverlist", GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ldaptimeout", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "|N|set LDAP timeout to N seconds",
+ GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR },
+ /* The following entry must not be removed, as it is required for
+ the GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST. */
+ { "ldapserverlist-file",
+ GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL,
+ "dirmngr", "|FILE|read LDAP server list from FILE",
+ GC_ARG_TYPE_PATHNAME, GC_BACKEND_DIRMNGR },
+ /* This entry must come after at least one entry for
+ GC_BACKEND_DIRMNGR in this component, so that the entry for
+ "ldapserverlist-file will be initialized before this one. */
+ { "LDAP Server", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_LIST, GC_LEVEL_BASIC,
+ NULL, "LDAP server list",
+ GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST },
+ { "max-replies", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "|N|do not return more than N items in one query",
+ GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR },
+
+ { "OCSP",
+ GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED,
+ "gnupg", N_("Configuration for OCSP") },
+ { "allow-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
+ "dirmngr", "allow sending OCSP requests",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ignore-ocsp-service-url", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "ignore certificate contained OCSP service URLs",
+ GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR },
+ { "ocsp-responder", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "|URL|use OCSP responder at URL",
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
+ { "ocsp-signer", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
+ "dirmngr", "|FPR|OCSP response signed by FPR",
+ GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR },
+
+
+ GC_OPTION_NULL
+ };
+
+
+/* Component system. Each component is a set of options that can be
+ configured at the same time. If you change this, don't forget to
+ update GC_COMPONENT below. */
+typedef enum
+ {
+ /* The classic GPG for OpenPGP. */
+ GC_COMPONENT_GPG,
+
+ /* The GPG Agent. */
+ GC_COMPONENT_GPG_AGENT,
+
+ /* The Smardcard Daemon. */
+ GC_COMPONENT_SCDAEMON,
+
+ /* GPG for S/MIME. */
+ GC_COMPONENT_GPGSM,
+
+ /* The LDAP Directory Manager for CRLs. */
+ GC_COMPONENT_DIRMNGR,
+
+ /* The number of components. */
+ GC_COMPONENT_NR
+ } gc_component_t;
+
+
+/* The information associated with each component. */
+static struct
+{
+ /* The name of this component. Must not contain a colon (':')
+ character. */
+ const char *name;
+
+ /* The gettext domain for the description DESC. If this is NULL,
+ then the description is not translated. */
+ const char *desc_domain;
+
+ /* The description for this domain. */
+ const char *desc;
+
+ /* The list of options for this component, terminated by
+ GC_OPTION_NULL. */
+ gc_option_t *options;
+} gc_component[] =
+ {
+ { "gpg", NULL, "GPG for OpenPGP", gc_options_gpg },
+ { "gpg-agent", NULL, "GPG Agent", gc_options_gpg_agent },
+ { "scdaemon", NULL, "Smartcard Daemon", gc_options_scdaemon },
+ { "gpgsm", NULL, "GPG for S/MIME", gc_options_gpgsm },
+ { "dirmngr", NULL, "Directory Manager", gc_options_dirmngr }
+ };
+
+
+/* Engine specific support. */
+void
+gpg_agent_runtime_change (void)
+{
+#ifndef HAVE_W32_SYSTEM
+ char *agent = getenv ("GPG_AGENT_INFO");
+ char *pid_str;
+ unsigned long pid_long;
+ char *tail;
+ pid_t pid;
+
+ if (!agent)
+ return;
+
+ pid_str = strchr (agent, ':');
+ if (!pid_str)
+ return;
+
+ pid_str++;
+ errno = 0;
+ pid_long = strtoul (pid_str, &tail, 0);
+ if (errno || (*tail != ':' && *tail != '\0'))
+ return;
+
+ pid = (pid_t) pid_long;
+
+ /* Check for overflow. */
+ if (pid_long != (unsigned long) pid)
+ return;
+
+ /* Ignore any errors here. */
+ kill (pid, SIGHUP);
+#endif /*!HAVE_W32_SYSTEM*/
+}
+
+
+/* More or less Robust version of dgettext. It has the side effect of
+ switching the codeset to utf-8 because this is what we want to
+ output. In theory it is posible to keep the orginal code set and
+ switch back for regular disgnostic output (redefine "_(" for that)
+ but given the natur of this tool, being something invoked from
+ other pograms, it does not make much sense. */
+static const char *
+my_dgettext (const char *domain, const char *msgid)
+{
+#ifdef ENABLE_NLS
+ if (domain)
+ {
+ static int switched_codeset;
+ char *text;
+
+ if (!switched_codeset)
+ {
+ switched_codeset = 1;
+ bind_textdomain_codeset (PACKAGE_GT, "utf-8");
+
+ bindtextdomain ("dirmngr", LOCALEDIR);
+ bind_textdomain_codeset ("dirmngr", "utf-8");
+
+ }
+
+ /* Note: This is a hack to actually use the gnupg2 domain as
+ long we are in a transition phase where gnupg 1.x and 1.9 may
+ coexist. */
+ if (!strcmp (domain, "gnupg"))
+ domain = PACKAGE_GT;
+
+ text = dgettext (domain, msgid);
+ return text ? text : msgid;
+ }
+ else
+#endif
+ return msgid;
+}
+
+
+/* Percent-Escape special characters. The string is valid until the
+ next invocation of the function. */
+static char *
+percent_escape (const char *src)
+{
+ static char *esc_str;
+ static int esc_str_len;
+ int new_len = 3 * strlen (src) + 1;
+ char *dst;
+
+ if (esc_str_len < new_len)
+ {
+ char *new_esc_str = realloc (esc_str, new_len);
+ if (!new_esc_str)
+ gc_error (1, errno, "can not escape string");
+ esc_str = new_esc_str;
+ esc_str_len = new_len;
+ }
+
+ dst = esc_str;
+ while (*src)
+ {
+ if (*src == '%')
+ {
+ *(dst++) = '%';
+ *(dst++) = '2';
+ *(dst++) = '5';
+ }
+ else if (*src == ':')
+ {
+ /* The colon is used as field separator. */
+ *(dst++) = '%';
+ *(dst++) = '3';
+ *(dst++) = 'a';
+ }
+ else if (*src == ',')
+ {
+ /* The comma is used as list separator. */
+ *(dst++) = '%';
+ *(dst++) = '2';
+ *(dst++) = 'c';
+ }
+ else
+ *(dst++) = *(src);
+ src++;
+ }
+ *dst = '\0';
+ return esc_str;
+}
+
+
+
+/* Percent-Deescape special characters. The string is valid until the
+ next invocation of the function. */
+static char *
+percent_deescape (const char *src)
+{
+ static char *str;
+ static int str_len;
+ int new_len = 3 * strlen (src) + 1;
+ char *dst;
+
+ if (str_len < new_len)
+ {
+ char *new_str = realloc (str, new_len);
+ if (!new_str)
+ gc_error (1, errno, "can not deescape string");
+ str = new_str;
+ str_len = new_len;
+ }
+
+ dst = str;
+ while (*src)
+ {
+ if (*src == '%')
+ {
+ int val = hextobyte (src + 1);
+
+ if (val < 0)
+ gc_error (1, 0, "malformed end of string %s", src);
+
+ *(dst++) = (char) val;
+ src += 3;
+ }
+ else
+ *(dst++) = *(src++);
+ }
+ *dst = '\0';
+ return str;
+}
+
+
+/* List all components that are available. */
+void
+gc_component_list_components (FILE *out)
+{
+ gc_component_t idx;
+
+ for (idx = 0; idx < GC_COMPONENT_NR; idx++)
+ {
+ const char *desc = gc_component[idx].desc;
+ desc = my_dgettext (gc_component[idx].desc_domain, desc);
+ fprintf (out, "%s:%s\n", gc_component[idx].name, percent_escape (desc));
+ }
+}
+
+
+/* Find the component with the name NAME. Returns -1 if not
+ found. */
+int
+gc_component_find (const char *name)
+{
+ gc_component_t idx;
+
+ for (idx = 0; idx < GC_COMPONENT_NR; idx++)
+ {
+ if (!strcmp (name, gc_component[idx].name))
+ return idx;
+ }
+ return -1;
+}
+
+
+/* List the option OPTION. */
+static void
+list_one_option (const gc_option_t *option, FILE *out)
+{
+ const char *desc = NULL;
+ char *arg_name = NULL;
+
+ if (option->desc)
+ {
+ desc = my_dgettext (option->desc_domain, option->desc);
+
+ if (*desc == '|')
+ {
+ const char *arg_tail = strchr (&desc[1], '|');
+
+ if (arg_tail)
+ {
+ int arg_len = arg_tail - &desc[1];
+ arg_name = xmalloc (arg_len + 1);
+ memcpy (arg_name, &desc[1], arg_len);
+ arg_name[arg_len] = '\0';
+ desc = arg_tail + 1;
+ }
+ }
+ }
+
+
+ /* YOU MUST NOT REORDER THE FIELDS IN THIS OUTPUT, AS THEIR ORDER IS
+ PART OF THE EXTERNAL INTERFACE. YOU MUST NOT REMOVE ANY
+ FIELDS. */
+
+ /* The name field. */
+ fprintf (out, "%s", option->name);
+
+ /* The flags field. */
+ fprintf (out, ":%lu", option->flags);
+ if (opt.verbose)
+ {
+ putc (' ', out);
+
+ if (!option->flags)
+ fprintf (out, "none");
+ else
+ {
+ unsigned long flags = option->flags;
+ unsigned long flag = 0;
+ unsigned long first = 1;
+
+ while (flags)
+ {
+ if (flags & 1)
+ {
+ if (first)
+ first = 0;
+ else
+ putc (',', out);
+ fprintf (out, "%s", gc_flag[flag].name);
+ }
+ flags >>= 1;
+ flag++;
+ }
+ }
+ }
+
+ /* The level field. */
+ fprintf (out, ":%u", option->level);
+ if (opt.verbose)
+ fprintf (out, " %s", gc_level[option->level].name);
+
+ /* The description field. */
+ fprintf (out, ":%s", desc ? percent_escape (desc) : "");
+
+ /* The type field. */
+ fprintf (out, ":%u", option->arg_type);
+ if (opt.verbose)
+ fprintf (out, " %s", gc_arg_type[option->arg_type].name);
+
+ /* The alternate type field. */
+ fprintf (out, ":%u", gc_arg_type[option->arg_type].fallback);
+ if (opt.verbose)
+ fprintf (out, " %s",
+ gc_arg_type[gc_arg_type[option->arg_type].fallback].name);
+
+ /* The argument name field. */
+ fprintf (out, ":%s", arg_name ? percent_escape (arg_name) : "");
+ if (arg_name)
+ xfree (arg_name);
+
+ /* The default value field. */
+ fprintf (out, ":%s", option->default_value ? option->default_value : "");
+
+ /* The default argument field. */
+ fprintf (out, ":%s", option->default_arg ? option->default_arg : "");
+
+ /* The value field. */
+ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE
+ && (option->flags & GC_OPT_FLAG_LIST)
+ && option->value)
+ /* The special format "1,1,1,1,...,1" is converted to a number
+ here. */
+ fprintf (out, ":%u", (strlen (option->value) + 1) / 2);
+ else
+ fprintf (out, ":%s", option->value ? option->value : "");
+
+ /* ADD NEW FIELDS HERE. */
+
+ putc ('\n', out);
+}
+
+
+/* List all options of the component COMPONENT. */
+void
+gc_component_list_options (int component, FILE *out)
+{
+ const gc_option_t *option = gc_component[component].options;
+ const gc_option_t *group_option = NULL;
+
+ while (option->name)
+ {
+ /* Do not output unknown or internal options. */
+ if (!(option->flags & GC_OPT_FLAG_GROUP)
+ && (!option->active || option->level == GC_LEVEL_INTERNAL))
+ {
+ option++;
+ continue;
+ }
+
+ if (option->flags & GC_OPT_FLAG_GROUP)
+ group_option = option;
+ else
+ {
+ if (group_option)
+ {
+ list_one_option (group_option, out);
+ group_option = NULL;
+ }
+
+ list_one_option (option, out);
+ }
+
+ option++;
+ }
+}
+
+
+/* Find the option NAME in component COMPONENT, for the backend
+ BACKEND. If BACKEND is GC_BACKEND_ANY, any backend will match. */
+static gc_option_t *
+find_option (gc_component_t component, const char *name,
+ gc_backend_t backend)
+{
+ gc_option_t *option = gc_component[component].options;
+ while (option->name)
+ {
+ if (!(option->flags & GC_OPT_FLAG_GROUP)
+ && !strcmp (option->name, name)
+ && (backend == GC_BACKEND_ANY || option->backend == backend))
+ break;
+ option++;
+ }
+ return option->name ? option : NULL;
+}
+
+
+/* Determine the configuration pathname for the component COMPONENT
+ and backend BACKEND. */
+static char *
+get_config_pathname (gc_component_t component, gc_backend_t backend)
+{
+ char *pathname = NULL;
+ gc_option_t *option = find_option
+ (component, gc_backend[backend].option_config_filename, GC_BACKEND_ANY);
+ assert (option);
+ assert (option->arg_type == GC_ARG_TYPE_PATHNAME);
+ assert (!(option->flags & GC_OPT_FLAG_LIST));
+
+ if (!option->active || !option->default_value)
+ gc_error (1, 0, "Option %s, needed by backend %s, was not initialized",
+ gc_backend[backend].option_config_filename,
+ gc_backend[backend].name);
+
+ if (option->value && *option->value)
+ pathname = percent_deescape (&option->value[1]);
+ else if (option->default_value && *option->default_value)
+ pathname = percent_deescape (&option->default_value[1]);
+ else
+ pathname = "";
+
+#ifdef HAVE_DOSISH_SYSTEM
+ if (!(pathname[0]
+ && pathname[1] == ':'
+ && (pathname[2] == '/' || pathname[2] == '\\')))
+#else
+ if (pathname[0] != '/')
+#endif
+ gc_error (1, 0, "Option %s, needed by backend %s, is not absolute",
+ gc_backend[backend].option_config_filename,
+ gc_backend[backend].name);
+
+ return pathname;
+}
+
+
+/* Retrieve the options for the component COMPONENT from backend
+ BACKEND, which we already know is a program-type backend. */
+static void
+retrieve_options_from_program (gc_component_t component, gc_backend_t backend)
+{
+ char *cmd_line;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+ FILE *config;
+ char *config_pathname;
+
+ cmd_line = xasprintf ("%s --gpgconf-list", gc_backend[backend].program);
+
+ config = popen (cmd_line, "r");
+ if (!config)
+ gc_error (1, errno, "could not gather active options from %s", cmd_line);
+
+ while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ {
+ gc_option_t *option;
+ char *linep;
+ unsigned long flags = 0;
+ char *default_value = NULL;
+
+ /* Strip newline and carriage return, if present. */
+ while (length > 0
+ && (line[length - 1] == '\n' || line[length - 1] == '\r'))
+ line[--length] = '\0';
+
+ linep = strchr (line, ':');
+ if (linep)
+ *(linep++) = '\0';
+
+ /* Extract additional flags. Default to none. */
+ if (linep)
+ {
+ char *end;
+ char *tail;
+
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+
+ errno = 0;
+ flags = strtoul (linep, &tail, 0);
+ if (errno)
+ gc_error (1, errno, "malformed flags in option %s from %s", line, cmd_line);
+ if (!(*tail == '\0' || *tail == ':' || *tail == ' '))
+ gc_error (1, 0, "garbage after flags in option %s from %s", line, cmd_line);
+
+ linep = end;
+ }
+
+ /* Extract default value, if present. Default to empty if
+ not. */
+ if (linep)
+ {
+ char *end;
+
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+
+ if (flags & GC_OPT_FLAG_DEFAULT)
+ default_value = linep;
+
+ linep = end;
+ }
+
+ /* Look up the option in the component and install the
+ configuration data. */
+ option = find_option (component, line, backend);
+ if (option)
+ {
+ if (option->active)
+ gc_error (1, errno, "option %s returned twice from %s",
+ line, cmd_line);
+ option->active = 1;
+
+ option->flags |= flags;
+ if (default_value && *default_value)
+ option->default_value = xstrdup (default_value);
+ }
+ }
+ if (length < 0 || ferror (config))
+ gc_error (1, errno, "error reading from %s", cmd_line);
+ if (fclose (config) && ferror (config))
+ gc_error (1, errno, "error closing %s", cmd_line);
+ xfree (cmd_line);
+
+ /* At this point, we can parse the configuration file. */
+ config_pathname = get_config_pathname (component, backend);
+
+ config = fopen (config_pathname, "r");
+ if (!config)
+ gc_error (0, errno, "warning: can not open config file %s",
+ config_pathname);
+ else
+ {
+ while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ {
+ char *name;
+ char *value;
+ gc_option_t *option;
+
+ name = line;
+ while (*name == ' ' || *name == '\t')
+ name++;
+ if (!*name || *name == '#' || *name == '\r' || *name == '\n')
+ continue;
+
+ value = name;
+ while (*value && *value != ' ' && *value != '\t'
+ && *value != '#' && *value != '\r' && *value != '\n')
+ value++;
+ if (*value == ' ' || *value == '\t')
+ {
+ char *end;
+
+ *(value++) = '\0';
+ while (*value == ' ' || *value == '\t')
+ value++;
+
+ end = value;
+ while (*end && *end != '#' && *end != '\r' && *end != '\n')
+ end++;
+ while (end > value && (end[-1] == ' ' || end[-1] == '\t'))
+ end--;
+ *end = '\0';
+ }
+ else
+ *value = '\0';
+
+ /* Look up the option in the component and install the
+ configuration data. */
+ option = find_option (component, line, backend);
+ if (option)
+ {
+ char *opt_value;
+
+ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE)
+ {
+ if (*value)
+ gc_error (0, 0,
+ "warning: ignoring argument %s for option %s",
+ value, name);
+ opt_value = xstrdup ("1");
+ }
+ else if (gc_arg_type[option->arg_type].fallback
+ == GC_ARG_TYPE_STRING)
+ opt_value = xasprintf ("\"%s", percent_escape (value));
+ else
+ {
+ /* FIXME: Verify that the number is sane. */
+ opt_value = xstrdup (value);
+ }
+
+ /* Now enter the option into the table. */
+ if (!(option->flags & GC_OPT_FLAG_LIST))
+ {
+ if (option->value)
+ free (option->value);
+ option->value = opt_value;
+ }
+ else
+ {
+ if (!option->value)
+ option->value = opt_value;
+ else
+ {
+ char *opt_val = opt_value;
+
+ option->value = xasprintf ("%s,%s", option->value,
+ opt_val);
+ xfree (opt_value);
+ }
+ }
+ }
+ }
+
+ if (length < 0 || ferror (config))
+ gc_error (1, errno, "error reading from %s", config_pathname);
+ if (fclose (config) && ferror (config))
+ gc_error (1, errno, "error closing %s", config_pathname);
+ }
+
+ xfree (line);
+}
+
+
+/* Retrieve the options for the component COMPONENT from backend
+ BACKEND, which we already know is of type file list. */
+static void
+retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
+{
+ gc_option_t *list_option;
+ char *list_pathname;
+ FILE *list_file;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+ char *list = NULL;
+
+ list_option = find_option (component,
+ gc_backend[backend].option_name, GC_BACKEND_ANY);
+ assert (list_option);
+ assert (!list_option->active);
+
+ list_pathname = get_config_pathname (component, backend);
+ list_file = fopen (list_pathname, "r");
+ if (!list_file)
+ gc_error (0, errno, "warning: can not open list file %s", list_pathname);
+ else
+ {
+
+ while ((length = read_line (list_file, &line, &line_len, NULL)) > 0)
+ {
+ char *start;
+ char *end;
+ char *new_list;
+
+ start = line;
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (!*start || *start == '#' || *start == '\r' || *start == '\n')
+ continue;
+
+ end = start;
+ while (*end && *end != '#' && *end != '\r' && *end != '\n')
+ end++;
+ /* Walk back to skip trailing white spaces. Looks evil, but
+ works because of the conditions on START and END imposed
+ at this point (END is at least START + 1, and START is
+ not a whitespace character). */
+ while (*(end - 1) == ' ' || *(end - 1) == '\t')
+ end--;
+ *end = '\0';
+ /* FIXME: Oh, no! This is so lame! Should use realloc and
+ really append. */
+ if (list)
+ {
+ new_list = xasprintf ("%s,\"%s", list, percent_escape (start));
+ xfree (list);
+ list = new_list;
+ }
+ else
+ list = xasprintf ("\"%s", percent_escape (start));
+ }
+ if (length < 0 || ferror (list_file))
+ gc_error (1, errno, "can not read list file %s", list_pathname);
+ }
+
+ list_option->active = 1;
+ list_option->value = list;
+
+ xfree (line);
+}
+
+
+/* Retrieve the currently active options and their defaults from all
+ involved backends for this component. */
+void
+gc_component_retrieve_options (int component)
+{
+ int backend_seen[GC_BACKEND_NR];
+ gc_backend_t backend;
+ gc_option_t *option = gc_component[component].options;
+
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ backend_seen[backend] = 0;
+
+ while (option->name)
+ {
+ if (!(option->flags & GC_OPT_FLAG_GROUP))
+ {
+ backend = option->backend;
+
+ if (backend_seen[backend])
+ {
+ option++;
+ continue;
+ }
+ backend_seen[backend] = 1;
+
+ assert (backend != GC_BACKEND_ANY);
+
+ if (gc_backend[backend].program)
+ retrieve_options_from_program (component, backend);
+ else
+ retrieve_options_from_file (component, backend);
+ }
+ option++;
+ }
+}
+
+
+/* Perform a simple validity check based on the type. Return in
+ NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
+ type GC_ARG_TYPE_NONE. */
+static void
+option_check_validity (gc_option_t *option, unsigned long flags,
+ char *new_value, unsigned long *new_value_nr)
+{
+ char *arg;
+
+ if (!option->active)
+ gc_error (1, 0, "option %s not supported by backend", option->name);
+
+ if (option->new_flags || option->new_value)
+ gc_error (1, 0, "option %s already changed", option->name);
+
+ if (flags & GC_OPT_FLAG_DEFAULT)
+ {
+ if (*new_value)
+ gc_error (1, 0, "argument %s provided for deleted option %s",
+ new_value, option->name);
+
+ return;
+ }
+
+ /* GC_ARG_TYPE_NONE options have special list treatment. */
+ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE)
+ {
+ char *tail;
+
+ errno = 0;
+ *new_value_nr = strtoul (new_value, &tail, 0);
+
+ if (errno)
+ gc_error (1, errno, "invalid argument for option %s",
+ option->name);
+ if (*tail)
+ gc_error (1, 0, "garbage after argument for option %s",
+ option->name);
+
+ if (!(option->flags & GC_OPT_FLAG_LIST))
+ {
+ if (*new_value_nr != 1)
+ gc_error (1, 0, "argument for non-list option %s of type 0 "
+ "(none) must be 1", option->name);
+ }
+ else
+ {
+ if (*new_value_nr == 0)
+ gc_error (1, 0, "argument for option %s of type 0 (none) "
+ "must be positive", option->name);
+ }
+
+ return;
+ }
+
+ arg = new_value;
+ do
+ {
+ if (*arg == '\0' || *arg == ',')
+ {
+ if (!(option->flags & GC_OPT_FLAG_ARG_OPT))
+ gc_error (1, 0, "argument required for option %s", option->name);
+
+ if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST))
+ gc_error (1, 0, "list found for non-list option %s", option->name);
+ }
+ else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING)
+ {
+ if (*arg != '"')
+ gc_error (1, 0, "string argument for option %s must begin "
+ "with a quote (\") character", option->name);
+ }
+ else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32)
+ {
+ errno = 0;
+ (void) strtol (arg, &arg, 0);
+
+ if (errno)
+ gc_error (1, errno, "invalid argument for option %s",
+ option->name);
+
+ if (*arg != '\0' && *arg != ',')
+ gc_error (1, 0, "garbage after argument for option %s",
+ option->name);
+ }
+ else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_INT32)
+ {
+ errno = 0;
+ (void) strtoul (arg, &arg, 0);
+
+ if (errno)
+ gc_error (1, errno, "invalid argument for option %s",
+ option->name);
+
+ if (*arg != '\0' && *arg != ',')
+ gc_error (1, 0, "garbage after argument for option %s",
+ option->name);
+ }
+ arg = strchr (arg, ',');
+ if (arg)
+ arg++;
+ }
+ while (arg && *arg);
+}
+
+
+/* Create and verify the new configuration file for the specified
+ backend and component. Returns 0 on success and -1 on error. */
+static int
+change_options_file (gc_component_t component, gc_backend_t backend,
+ char **src_filenamep, char **dest_filenamep,
+ char **orig_filenamep)
+{
+ static const char marker[] = "###+++--- GPGConf ---+++###";
+ /* True if we are within the marker in the config file. */
+ int in_marker = 0;
+ gc_option_t *option;
+ char *line = NULL;
+ size_t line_len;
+ ssize_t length;
+ int res;
+ int fd;
+ FILE *src_file = NULL;
+ FILE *dest_file = NULL;
+ char *src_filename;
+ char *dest_filename;
+ char *orig_filename;
+ char *arg;
+ char *cur_arg = NULL;
+
+ option = find_option (component,
+ gc_backend[backend].option_name, GC_BACKEND_ANY);
+ assert (option);
+ assert (option->active);
+ assert (gc_arg_type[option->arg_type].fallback != GC_ARG_TYPE_NONE);
+
+ /* FIXME. Throughout the function, do better error reporting. */
+ /* Note that get_config_pathname() calls percent_deescape(), so we
+ call this before processing the arguments. */
+ dest_filename = xstrdup (get_config_pathname (component, backend));
+ src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ());
+ orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ());
+
+ arg = option->new_value;
+ if (arg && arg[0] == '\0')
+ arg = NULL;
+ else if (arg)
+ {
+ char *end;
+
+ arg++;
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+
+ cur_arg = percent_deescape (arg);
+ if (end)
+ {
+ *end = ',';
+ arg = end + 1;
+ }
+ else
+ arg = NULL;
+ }
+
+#if HAVE_W32_SYSTEM
+ res = 0;
+#warning no backups for W32 yet - need to write a copy function
+#else
+ res = link (dest_filename, orig_filename);
+#endif
+ if (res < 0 && errno != ENOENT)
+ return -1;
+ if (res < 0)
+ {
+ xfree (orig_filename);
+ orig_filename = NULL;
+ }
+
+ /* We now initialize the return strings, so the caller can do the
+ cleanup for us. */
+ *src_filenamep = src_filename;
+ *dest_filenamep = dest_filename;
+ *orig_filenamep = orig_filename;
+
+ /* Use open() so that we can use O_EXCL. */
+ fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
+ if (fd < 0)
+ return -1;
+ src_file = fdopen (fd, "w");
+ res = errno;
+ if (!src_file)
+ {
+ errno = res;
+ return -1;
+ }
+
+ /* Only if ORIG_FILENAME is not NULL did the configuration file
+ exist already. In this case, we will copy its content into the
+ new configuration file, changing it to our liking in the
+ process. */
+ if (orig_filename)
+ {
+ dest_file = fopen (dest_filename, "r");
+ if (!dest_file)
+ goto change_file_one_err;
+
+ while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ {
+ int disable = 0;
+ char *start;
+
+ if (!strncmp (marker, line, sizeof (marker) - 1))
+ {
+ if (!in_marker)
+ in_marker = 1;
+ else
+ break;
+ }
+
+ start = line;
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start && *start != '\r' && *start != '\n' && *start != '#')
+ {
+ char *end;
+ char *endp;
+ char saved_end;
+
+ endp = start;
+ end = endp;
+
+ /* Search for the end of the line. */
+ while (*endp && *endp != '#' && *endp != '\r' && *endp != '\n')
+ {
+ endp++;
+ if (*endp && *endp != ' ' && *endp != '\t'
+ && *endp != '\r' && *endp != '\n' && *endp != '#')
+ end = endp + 1;
+ }
+ saved_end = *end;
+ *end = '\0';
+
+ if ((option->new_flags & GC_OPT_FLAG_DEFAULT)
+ || !cur_arg || strcmp (start, cur_arg))
+ disable = 1;
+ else
+ {
+ /* Find next argument. */
+ if (arg)
+ {
+ char *arg_end;
+
+ arg++;
+ arg_end = strchr (arg, ',');
+ if (arg_end)
+ *arg_end = '\0';
+
+ cur_arg = percent_deescape (arg);
+ if (arg_end)
+ {
+ *arg_end = ',';
+ arg = arg_end + 1;
+ }
+ else
+ arg = NULL;
+ }
+ else
+ cur_arg = NULL;
+ }
+
+ *end = saved_end;
+ }
+
+ if (disable)
+ {
+ if (!in_marker)
+ {
+ fprintf (src_file,
+ "# GPGConf disabled this option here at %s\n",
+ asctimestamp (gnupg_get_time ()));
+ if (ferror (src_file))
+ goto change_file_one_err;
+ fprintf (src_file, "# %s", line);
+ if (ferror (src_file))
+ goto change_file_one_err;
+ }
+ }
+ else
+ {
+ fprintf (src_file, "%s", line);
+ if (ferror (src_file))
+ goto change_file_one_err;
+ }
+ }
+ if (length < 0 || ferror (dest_file))
+ goto change_file_one_err;
+ }
+
+ if (!in_marker)
+ {
+ /* There was no marker. This is the first time we edit the
+ file. We add our own marker at the end of the file and
+ proceed. Note that we first write a newline, this guards us
+ against files which lack the newline at the end of the last
+ line, while it doesn't hurt us in all other cases. */
+ fprintf (src_file, "\n%s\n", marker);
+ if (ferror (src_file))
+ goto change_file_one_err;
+ }
+
+ /* At this point, we have copied everything up to the end marker
+ into the new file, except for the arguments we are going to add.
+ Now, dump the new arguments and write the end marker, possibly
+ followed by the rest of the original file. */
+ while (cur_arg)
+ {
+ fprintf (src_file, "%s\n", cur_arg);
+
+ /* Find next argument. */
+ if (arg)
+ {
+ char *end;
+
+ arg++;
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+
+ cur_arg = percent_deescape (arg);
+ if (end)
+ {
+ *end = ',';
+ arg = end + 1;
+ }
+ else
+ arg = NULL;
+ }
+ else
+ cur_arg = NULL;
+ }
+
+ fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+ if (ferror (src_file))
+ goto change_file_one_err;
+
+ if (!in_marker)
+ {
+ fprintf (src_file, "# GPGConf edited this configuration file.\n");
+ if (ferror (src_file))
+ goto change_file_one_err;
+ fprintf (src_file, "# It will disable options before this marked "
+ "block, but it will\n");
+ if (ferror (src_file))
+ goto change_file_one_err;
+ fprintf (src_file, "# never change anything below these lines.\n");
+ if (ferror (src_file))
+ goto change_file_one_err;
+ }
+ if (dest_file)
+ {
+ while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ {
+ fprintf (src_file, "%s", line);
+ if (ferror (src_file))
+ goto change_file_one_err;
+ }
+ if (length < 0 || ferror (dest_file))
+ goto change_file_one_err;
+ }
+ xfree (line);
+ line = NULL;
+
+ res = fclose (src_file);
+ if (res)
+ {
+ res = errno;
+ close (fd);
+ if (dest_file)
+ fclose (dest_file);
+ errno = res;
+ return -1;
+ }
+ close (fd);
+ if (dest_file)
+ {
+ res = fclose (dest_file);
+ if (res)
+ return -1;
+ }
+ return 0;
+
+ change_file_one_err:
+ xfree (line);
+ res = errno;
+ if (src_file)
+ {
+ fclose (src_file);
+ close (fd);
+ }
+ if (dest_file)
+ fclose (dest_file);
+ errno = res;
+ return -1;
+}
+
+
+/* Create and verify the new configuration file for the specified
+ backend and component. Returns 0 on success and -1 on error. */
+static int
+change_options_program (gc_component_t component, gc_backend_t backend,
+ char **src_filenamep, char **dest_filenamep,
+ char **orig_filenamep)
+{
+ static const char marker[] = "###+++--- GPGConf ---+++###";
+ /* True if we are within the marker in the config file. */
+ int in_marker = 0;
+ gc_option_t *option;
+ char *line = NULL;
+ size_t line_len;
+ ssize_t length;
+ int res;
+ int fd;
+ FILE *src_file = NULL;
+ FILE *dest_file = NULL;
+ char *src_filename;
+ char *dest_filename;
+ char *orig_filename;
+
+ /* FIXME. Throughout the function, do better error reporting. */
+ dest_filename = xstrdup (get_config_pathname (component, backend));
+ src_filename = xasprintf ("%s.gpgconf.%i.new", dest_filename, getpid ());
+ orig_filename = xasprintf ("%s.gpgconf.%i.bak", dest_filename, getpid ());
+
+#if HAVE_W32_SYSTEM
+ res = 0;
+#warning no backups for W32 yet - need to write a copy function
+#else
+ res = link (dest_filename, orig_filename);
+#endif
+ if (res < 0 && errno != ENOENT)
+ return -1;
+ if (res < 0)
+ {
+ xfree (orig_filename);
+ orig_filename = NULL;
+ }
+
+ /* We now initialize the return strings, so the caller can do the
+ cleanup for us. */
+ *src_filenamep = src_filename;
+ *dest_filenamep = dest_filename;
+ *orig_filenamep = orig_filename;
+
+ /* Use open() so that we can use O_EXCL. */
+ fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644);
+ if (fd < 0)
+ return -1;
+ src_file = fdopen (fd, "w");
+ res = errno;
+ if (!src_file)
+ {
+ errno = res;
+ return -1;
+ }
+
+ /* Only if ORIG_FILENAME is not NULL did the configuration file
+ exist already. In this case, we will copy its content into the
+ new configuration file, changing it to our liking in the
+ process. */
+ if (orig_filename)
+ {
+ dest_file = fopen (dest_filename, "r");
+ if (!dest_file)
+ goto change_one_err;
+
+ while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ {
+ int disable = 0;
+ char *start;
+
+ if (!strncmp (marker, line, sizeof (marker) - 1))
+ {
+ if (!in_marker)
+ in_marker = 1;
+ else
+ break;
+ }
+
+ start = line;
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (*start && *start != '\r' && *start != '\n' && *start != '#')
+ {
+ char *end;
+ char saved_end;
+
+ end = start;
+ while (*end && *end != ' ' && *end != '\t'
+ && *end != '\r' && *end != '\n' && *end != '#')
+ end++;
+ saved_end = *end;
+ *end = '\0';
+
+ option = find_option (component, start, backend);
+ *end = saved_end;
+ if (option && ((option->new_flags & GC_OPT_FLAG_DEFAULT)
+ || option->new_value))
+ disable = 1;
+ }
+ if (disable)
+ {
+ if (!in_marker)
+ {
+ fprintf (src_file,
+ "# GPGConf disabled this option here at %s\n",
+ asctimestamp (gnupg_get_time ()));
+ if (ferror (src_file))
+ goto change_one_err;
+ fprintf (src_file, "# %s", line);
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ }
+ else
+ {
+ fprintf (src_file, "%s", line);
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ }
+ if (length < 0 || ferror (dest_file))
+ goto change_one_err;
+ }
+
+ if (!in_marker)
+ {
+ /* There was no marker. This is the first time we edit the
+ file. We add our own marker at the end of the file and
+ proceed. Note that we first write a newline, this guards us
+ against files which lack the newline at the end of the last
+ line, while it doesn't hurt us in all other cases. */
+ fprintf (src_file, "\n%s\n", marker);
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ /* At this point, we have copied everything up to the end marker
+ into the new file, except for the options we are going to change.
+ Now, dump the changed options (except for those we are going to
+ revert to their default), and write the end marker, possibly
+ followed by the rest of the original file. */
+
+ /* We have to turn on UTF8 strings for GnuPG. */
+ if (backend == GC_BACKEND_GPG)
+ fprintf (src_file, "utf8-strings\n");
+
+ option = gc_component[component].options;
+ while (option->name)
+ {
+ if (!(option->flags & GC_OPT_FLAG_GROUP)
+ && option->backend == backend
+ && option->new_value)
+ {
+ char *arg = option->new_value;
+
+ do
+ {
+ if (*arg == '\0' || *arg == ',')
+ {
+ fprintf (src_file, "%s\n", option->name);
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ else if (gc_arg_type[option->arg_type].fallback
+ == GC_ARG_TYPE_NONE)
+ {
+ assert (*arg == '1');
+ fprintf (src_file, "%s\n", option->name);
+ if (ferror (src_file))
+ goto change_one_err;
+
+ arg++;
+ }
+ else if (gc_arg_type[option->arg_type].fallback
+ == GC_ARG_TYPE_STRING)
+ {
+ char *end;
+
+ assert (*arg == '"');
+ arg++;
+
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+
+ fprintf (src_file, "%s %s\n", option->name,
+ percent_deescape (arg));
+ if (ferror (src_file))
+ goto change_one_err;
+
+ if (end)
+ *end = ',';
+ arg = end;
+ }
+ else
+ {
+ char *end;
+
+ end = strchr (arg, ',');
+ if (end)
+ *end = '\0';
+
+ fprintf (src_file, "%s %s\n", option->name, arg);
+ if (ferror (src_file))
+ goto change_one_err;
+
+ if (end)
+ *end = ',';
+ arg = end;
+ }
+
+ assert (arg == NULL || *arg == '\0' || *arg == ',');
+ if (arg && *arg == ',')
+ arg++;
+ }
+ while (arg && *arg);
+ }
+ option++;
+ }
+
+ fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ()));
+ if (ferror (src_file))
+ goto change_one_err;
+
+ if (!in_marker)
+ {
+ fprintf (src_file, "# GPGConf edited this configuration file.\n");
+ if (ferror (src_file))
+ goto change_one_err;
+ fprintf (src_file, "# It will disable options before this marked "
+ "block, but it will\n");
+ if (ferror (src_file))
+ goto change_one_err;
+ fprintf (src_file, "# never change anything below these lines.\n");
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ if (dest_file)
+ {
+ while ((length = read_line (dest_file, &line, &line_len, NULL)) > 0)
+ {
+ fprintf (src_file, "%s", line);
+ if (ferror (src_file))
+ goto change_one_err;
+ }
+ if (length < 0 || ferror (dest_file))
+ goto change_one_err;
+ }
+ xfree (line);
+ line = NULL;
+
+ res = fclose (src_file);
+ if (res)
+ {
+ res = errno;
+ close (fd);
+ if (dest_file)
+ fclose (dest_file);
+ errno = res;
+ return -1;
+ }
+ close (fd);
+ if (dest_file)
+ {
+ res = fclose (dest_file);
+ if (res)
+ return -1;
+ }
+ return 0;
+
+ change_one_err:
+ xfree (line);
+ res = errno;
+ if (src_file)
+ {
+ fclose (src_file);
+ close (fd);
+ }
+ if (dest_file)
+ fclose (dest_file);
+ errno = res;
+ return -1;
+}
+
+
+/* Read the modifications from IN and apply them. */
+void
+gc_component_change_options (int component, FILE *in)
+{
+ int err = 0;
+ int runtime[GC_BACKEND_NR];
+ char *src_pathname[GC_BACKEND_NR];
+ char *dest_pathname[GC_BACKEND_NR];
+ char *orig_pathname[GC_BACKEND_NR];
+ gc_backend_t backend;
+ gc_option_t *option;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ {
+ runtime[backend] = 0;
+ src_pathname[backend] = NULL;
+ dest_pathname[backend] = NULL;
+ orig_pathname[backend] = NULL;
+ }
+
+ while ((length = read_line (in, &line, &line_len, NULL)) > 0)
+ {
+ char *linep;
+ unsigned long flags = 0;
+ char *new_value = "";
+ unsigned long new_value_nr = 0;
+
+ /* Strip newline and carriage return, if present. */
+ while (length > 0
+ && (line[length - 1] == '\n' || line[length - 1] == '\r'))
+ line[--length] = '\0';
+
+ linep = strchr (line, ':');
+ if (linep)
+ *(linep++) = '\0';
+
+ /* Extract additional flags. Default to none. */
+ if (linep)
+ {
+ char *end;
+ char *tail;
+
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+
+ errno = 0;
+ flags = strtoul (linep, &tail, 0);
+ if (errno)
+ gc_error (1, errno, "malformed flags in option %s", line);
+ if (!(*tail == '\0' || *tail == ':' || *tail == ' '))
+ gc_error (1, 0, "garbage after flags in option %s", line);
+
+ linep = end;
+ }
+
+ /* Extract default value, if present. Default to empty if
+ not. */
+ if (linep)
+ {
+ char *end;
+
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+
+ new_value = linep;
+
+ linep = end;
+ }
+
+ option = find_option (component, line, GC_BACKEND_ANY);
+ if (!option)
+ gc_error (1, 0, "unknown option %s", line);
+
+ option_check_validity (option, flags, new_value, &new_value_nr);
+
+ if (option->flags & GC_OPT_FLAG_RUNTIME)
+ runtime[option->backend] = 1;
+
+ option->new_flags = flags;
+ if (!(flags & GC_OPT_FLAG_DEFAULT))
+ {
+ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE
+ && (option->flags & GC_OPT_FLAG_LIST))
+ {
+ char *str;
+
+ /* We convert the number to a list of 1's for
+ convenient list handling. */
+ assert (new_value_nr > 0);
+ option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1);
+ str = option->new_value;
+ *(str++) = '1';
+ while (--new_value_nr > 0)
+ {
+ *(str++) = ',';
+ *(str++) = '1';
+ }
+ *(str++) = '\0';
+ }
+ else
+ option->new_value = xstrdup (new_value);
+ }
+ }
+
+ /* Now that we have collected and locally verified the changes,
+ write them out to new configuration files, verify them
+ externally, and then commit them. */
+ option = gc_component[component].options;
+ while (option->name)
+ {
+ /* Go on if we have already seen this backend, or if there is
+ nothing to do. */
+ if (src_pathname[option->backend]
+ || !(option->new_flags || option->new_value))
+ {
+ option++;
+ continue;
+ }
+
+ if (gc_backend[option->backend].program)
+ err = change_options_program (component, option->backend,
+ &src_pathname[option->backend],
+ &dest_pathname[option->backend],
+ &orig_pathname[option->backend]);
+ else
+ err = change_options_file (component, option->backend,
+ &src_pathname[option->backend],
+ &dest_pathname[option->backend],
+ &orig_pathname[option->backend]);
+
+ if (err)
+ break;
+
+ option++;
+ }
+
+ if (!err)
+ {
+ int i;
+
+ for (i = 0; i < GC_BACKEND_NR; i++)
+ {
+ if (src_pathname[i])
+ {
+ /* FIXME: Make a verification here. */
+
+ assert (dest_pathname[i]);
+
+ if (orig_pathname[i])
+ err = rename (src_pathname[i], dest_pathname[i]);
+ else
+ {
+#ifdef HAVE_W32_SYSTEM
+ /* FIXME: Won't work becuase W32 doesn't silently
+ overwrite. Fix it by creating a backup copy and
+ deliting the orginal file first. */
+ err = rename (src_pathname[i], dest_pathname[i]);
+#else /*!HAVE_W32_SYSTEM*/
+ /* This is a bit safer than rename() because we
+ expect DEST_PATHNAME not to be there. If it
+ happens to be there, this will fail. */
+ err = link (src_pathname[i], dest_pathname[i]);
+ if (!err)
+ unlink (src_pathname[i]);
+#endif /*!HAVE_W32_SYSTEM*/
+ }
+ if (err)
+ break;
+ src_pathname[i] = NULL;
+ }
+ }
+ }
+
+ if (err)
+ {
+ int i;
+ int saved_errno = errno;
+
+ /* An error occured. */
+ for (i = 0; i < GC_BACKEND_NR; i++)
+ {
+ if (src_pathname[i])
+ {
+ /* The change was not yet committed. */
+ unlink (src_pathname[i]);
+ if (orig_pathname[i])
+ unlink (orig_pathname[i]);
+ }
+ else
+ {
+ /* The changes were already committed. FIXME: This is a
+ tad dangerous, as we don't know if we don't overwrite
+ a version of the file that is even newer than the one
+ we just installed. */
+ if (orig_pathname[i])
+ rename (orig_pathname[i], dest_pathname[i]);
+ else
+ unlink (dest_pathname[i]);
+ }
+ }
+ gc_error (1, saved_errno, "could not commit changes");
+ }
+
+ /* If it all worked, notify the daemons of the changes. */
+ if (opt.runtime)
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ {
+ if (runtime[backend] && gc_backend[backend].runtime_change)
+ (*gc_backend[backend].runtime_change) ();
+ }
+
+ /* Move the per-process backup file into its place. */
+ for (backend = 0; backend < GC_BACKEND_NR; backend++)
+ if (orig_pathname[backend])
+ {
+ char *backup_pathname;
+
+ assert (dest_pathname[backend]);
+
+ backup_pathname = xasprintf ("%s.gpgconf.bak", dest_pathname[backend]);
+ rename (orig_pathname[backend], backup_pathname);
+ }
+
+ xfree (line);
+}
diff --git a/tools/gpgconf.c b/tools/gpgconf.c
new file mode 100644
index 000000000..87ba45ae1
--- /dev/null
+++ b/tools/gpgconf.c
@@ -0,0 +1,203 @@
+/* gpgconf.c - Configuration utility for GnuPG
+ * 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.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "gpgconf.h"
+#include "i18n.h"
+
+/* Constants to identify the commands and options. */
+enum cmd_and_opt_values
+ {
+ aNull = 0,
+ oDryRun = 'n',
+ oOutput = 'o',
+ oQuiet = 'q',
+ oVerbose = 'v',
+ oRuntime = 'r',
+ oComponent = 'c',
+ oNoVerbose = 500,
+ oHomedir,
+
+ aListComponents,
+ aListOptions,
+ aChangeOptions,
+
+ };
+
+
+/* The list of commands and options. */
+static ARGPARSE_OPTS opts[] =
+ {
+ { 300, NULL, 0, N_("@Commands:\n ") },
+
+ { aListComponents, "list-components", 256, N_("list all components") },
+ { aListOptions, "list-options", 256, N_("|COMPONENT|list options") },
+ { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") },
+
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oOutput, "output", 2, N_("use as output file") },
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("quiet") },
+ { oDryRun, "dry-run", 0, N_("do not make any changes") },
+ { oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") },
+
+ /* hidden options */
+ { oNoVerbose, "no-verbose", 0, "@"},
+ {0}
+ };
+
+
+/* Print usage information and and provide strings for help. */
+static const char *
+my_strusage( int level )
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 11: p = "gpgconf (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: gpgconf [options] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: gpgconf [options]\n"
+ "Manage configuration options for tools of the GnuPG system\n");
+ break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+/* Initialize the gettext system. */
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE_GT);
+#else
+# ifdef ENABLE_NLS
+ setlocale (LC_ALL, "" );
+ bindtextdomain (PACKAGE_GT, LOCALEDIR);
+ textdomain (PACKAGE_GT);
+# endif
+#endif
+}
+
+
+/* gpgconf main. */
+int
+main (int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ const char *fname;
+ int no_more_options = 0;
+ enum cmd_and_opt_values cmd = 0;
+
+ set_strusage (my_strusage);
+ log_set_prefix ("gpgconf", 1);
+
+ i18n_init();
+
+ /* Parse the command line. */
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags = 1; /* Do not remove the args. */
+ while (!no_more_options && optfile_parse (NULL, NULL, NULL, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oOutput: opt.outfile = pargs.r.ret_str; break;
+ case oQuiet: opt.quiet = 1; break;
+ case oDryRun: opt.dry_run = 1; break;
+ case oRuntime:
+ opt.runtime = 1;
+ break;
+ case oVerbose: opt.verbose++; break;
+ case oNoVerbose: opt.verbose = 0; break;
+
+ case aListComponents:
+ case aListOptions:
+ case aChangeOptions:
+ cmd = pargs.r_opt;
+ break;
+
+ default: pargs.err = 2; break;
+ }
+ }
+
+ if (log_get_errorcount (0))
+ exit (2);
+
+ fname = argc ? *argv : NULL;
+
+ switch (cmd)
+ {
+ case aListComponents:
+ default:
+ /* List all components. */
+ gc_component_list_components (stdout);
+ break;
+
+ case aListOptions:
+ case aChangeOptions:
+ if (!fname)
+ {
+ fputs (_("usage: gpgconf [options] "), stderr);
+ putc ('\n',stderr);
+ fputs (_("Need one component argument"), stderr);
+ putc ('\n',stderr);
+ exit (2);
+ }
+ else
+ {
+ int idx = gc_component_find (fname);
+ if (idx < 0)
+ {
+ fputs (_("Component not found"), stderr);
+ putc ('\n', stderr);
+ exit (1);
+ }
+ gc_component_retrieve_options (idx);
+ if (cmd == aListOptions)
+ gc_component_list_options (idx, stdout);
+ else
+ gc_component_change_options (idx, stdin);
+ }
+ }
+
+ return 0;
+}
+
+
+
diff --git a/tools/gpgconf.h b/tools/gpgconf.h
new file mode 100644
index 000000000..c083c26aa
--- /dev/null
+++ b/tools/gpgconf.h
@@ -0,0 +1,59 @@
+/* gpgconf.h - Global definitions for gpgconf
+ * 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 GPGCONF_H
+#define GPGCONF_H
+
+#include "../common/util.h"
+
+/* We keep all global options in the structure OPT. */
+struct
+{
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be extra quiet. */
+ int dry_run; /* Don't change any persistent data. */
+ int runtime; /* Make changes active at runtime. */
+ char *outfile; /* Name of output file. */
+
+ int component; /* The active component. */
+} opt;
+
+
+
+/*-- gpgconf-comp.c --*/
+/* List all components that are available. */
+void gc_component_list_components (FILE *out);
+
+/* Find the component with the name NAME. Returns -1 if not
+ found. */
+int gc_component_find (const char *name);
+
+/* Retrieve the currently active options and their defaults from all
+ involved backends for this component. */
+void gc_component_retrieve_options (int component);
+
+/* List all options of the component COMPONENT. */
+void gc_component_list_options (int component, FILE *out);
+
+/* Read the modifications from IN and apply them. */
+void gc_component_change_options (int component, FILE *in);
+
+#endif /*GPGCONF_H*/
diff --git a/tools/gpgkey2ssh.c b/tools/gpgkey2ssh.c
new file mode 100644
index 000000000..3dcb6516e
--- /dev/null
+++ b/tools/gpgkey2ssh.c
@@ -0,0 +1,298 @@
+/* gpgkey2ssh.c - Converter ...
+ * 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 <gcrypt.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include "util.h"
+
+
+
+typedef struct pkdbuf
+{
+ unsigned char *buffer;
+ size_t buffer_n;
+} pkdbuf_t;
+
+
+
+/* Retrieve the public key material for the RSA key, whose fingerprint
+ is FPR, from gpg output, which can be read through the stream FP.
+ The RSA modulus will be stored at the address of M and MLEN, the
+ public exponent at E and ELEN. Returns zero on success, an error
+ code on failure. Caller must release the allocated buffers at M
+ and E if the function returns success. */
+static gpg_error_t
+retrieve_key_material (FILE *fp, const char *hexkeyid, int *algorithm_id,
+ pkdbuf_t **pkdbuf, size_t *pkdbuf_n)
+{
+ pkdbuf_t *pkdbuf_new;
+ pkdbuf_t *pkdbuf_tmp;
+ size_t pkdbuf_new_n;
+ gcry_error_t err = 0;
+ char *line = NULL; /* read_line() buffer. */
+ size_t line_size = 0; /* Helper for for read_line. */
+ int found_key = 0; /* Helper to find a matching key. */
+ int id;
+ unsigned char *buffer;
+ size_t buffer_n;
+ int i;
+
+ pkdbuf_new = NULL;
+ pkdbuf_new_n = 0;
+ id = 0;
+
+ /* Loop over all records until we have found the subkey
+ corresponsing to the fingerprint. Inm general the first record
+ should be the pub record, but we don't rely on that. Given that
+ we only need to look at one key, it is sufficient to compare the
+ keyid so that we don't need to look at "fpr" records. */
+ for (;;)
+ {
+ char *p;
+ char *fields[6];
+ int nfields;
+ size_t max_length;
+ gcry_mpi_t mpi;
+
+ max_length = 4096;
+ i = read_line (fp, &line, &line_size, &max_length);
+ if (!i)
+ break; /* EOF. */
+ if (i < 0)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave; /* Error. */
+ }
+ if (!max_length)
+ {
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave; /* Line truncated - we better stop processing. */
+ }
+
+ /* Parse the line into fields. */
+ for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
+ {
+ fields[nfields] = p;
+ p = strchr (p, ':');
+ if (p)
+ *(p++) = 0;
+ }
+ if (!nfields)
+ continue; /* No fields at all - skip line. */
+
+ if (!found_key)
+ {
+ if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ && nfields > 4 &&
+ (((strlen (hexkeyid) == 8)
+ && (strlen (fields[4]) == 16)
+ && (! strcmp (fields[4] + 8, hexkeyid)))
+ || ((strlen (hexkeyid) == 16)
+ && (! strcmp (fields[4], hexkeyid)))))
+ {
+ found_key = 1;
+ /* Save algorithm ID. */
+ id = atoi (fields[3]);
+ }
+ continue;
+ }
+
+ if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ break; /* Next key - stop. */
+
+ if ( strcmp (fields[0], "pkd") )
+ continue; /* Not a key data record. */
+
+ /* FIXME, necessary? */
+
+ i = atoi (fields[1]);
+ if ((nfields < 4) || (i < 0))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
+ if (err)
+ mpi = NULL;
+
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &buffer, &buffer_n, mpi);
+ gcry_mpi_release (mpi);
+ if (err)
+ goto leave;
+
+ pkdbuf_tmp = xrealloc (pkdbuf_new, sizeof (*pkdbuf_new) * (pkdbuf_new_n + 1));
+ if (pkdbuf_new != pkdbuf_tmp)
+ pkdbuf_new = pkdbuf_tmp;
+ pkdbuf_new[pkdbuf_new_n].buffer = buffer;
+ pkdbuf_new[pkdbuf_new_n].buffer_n = buffer_n;
+ pkdbuf_new_n++;
+ }
+
+ *algorithm_id = id;
+ *pkdbuf = pkdbuf_new;
+ *pkdbuf_n = pkdbuf_new_n;
+
+ leave:
+
+ if (err)
+ if (pkdbuf_new)
+ {
+ for (i = 0; i < pkdbuf_new_n; i++)
+ xfree (pkdbuf_new[i].buffer);
+ xfree (pkdbuf_new);
+ }
+ xfree (line);
+
+ return err;
+}
+
+
+
+int
+key_to_blob (unsigned char **blob, size_t *blob_n, const char *identifier, ...)
+{
+ unsigned char *blob_new;
+ size_t blob_new_n;
+ unsigned char uint32_buffer[4];
+ u32 identifier_n;
+ FILE *stream;
+ va_list ap;
+ int ret;
+ pkdbuf_t *pkd;
+
+ stream = tmpfile ();
+ assert (stream);
+
+ identifier_n = strlen (identifier);
+ uint32_buffer[0] = identifier_n >> 24;
+ uint32_buffer[1] = identifier_n >> 16;
+ uint32_buffer[2] = identifier_n >> 8;
+ uint32_buffer[3] = identifier_n >> 0;
+ ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
+ assert (ret == 1);
+ ret = fwrite (identifier, identifier_n, 1, stream);
+ assert (ret == 1);
+
+ va_start (ap, identifier);
+ while (1)
+ {
+ pkd = va_arg (ap, pkdbuf_t *);
+ if (! pkd)
+ break;
+
+ uint32_buffer[0] = pkd->buffer_n >> 24;
+ uint32_buffer[1] = pkd->buffer_n >> 16;
+ uint32_buffer[2] = pkd->buffer_n >> 8;
+ uint32_buffer[3] = pkd->buffer_n >> 0;
+ ret = fwrite (uint32_buffer, sizeof (uint32_buffer), 1, stream);
+ assert (ret == 1);
+ ret = fwrite (pkd->buffer, pkd->buffer_n, 1, stream);
+ assert (ret == 1);
+ }
+
+ blob_new_n = ftell (stream);
+ rewind (stream);
+
+ blob_new = xmalloc (blob_new_n);
+ ret = fread (blob_new, blob_new_n, 1, stream);
+ assert (ret == 1);
+
+ *blob = blob_new;
+ *blob_n = blob_new_n;
+
+ fclose (stream);
+
+ return 0;
+}
+
+int
+main (int argc, char **argv)
+{
+ const char *keyid;
+ int algorithm_id;
+ pkdbuf_t *pkdbuf;
+ size_t pkdbuf_n;
+ char *command;
+ FILE *fp;
+ int ret;
+ gcry_error_t err;
+ unsigned char *blob;
+ size_t blob_n;
+ struct b64state b64_state;
+ const char *identifier;
+
+ pkdbuf = NULL;
+ pkdbuf_n = 0;
+
+ algorithm_id = 0; /* (avoid cc warning) */
+ identifier = NULL; /* (avoid cc warning) */
+
+ assert (argc == 2);
+
+ keyid = argv[1];
+
+ ret = asprintf (&command,
+ "gpg --list-keys --with-colons --with-key-data '%s'",
+ keyid);
+ assert (ret > 0);
+
+ fp = popen (command, "r");
+ assert (fp);
+
+ err = retrieve_key_material (fp, keyid, &algorithm_id, &pkdbuf, &pkdbuf_n);
+ assert (! err);
+ assert ((algorithm_id == 1) || (algorithm_id == 17));
+
+ if (algorithm_id == 1)
+ {
+ identifier = "ssh-rsa";
+ ret = key_to_blob (&blob, &blob_n, identifier,
+ &pkdbuf[0], &pkdbuf[1], NULL);
+ }
+ else if (algorithm_id == 17)
+ {
+ identifier = "ssh-dsa";
+ ret = key_to_blob (&blob, &blob_n, identifier,
+ &pkdbuf[0], &pkdbuf[1], &pkdbuf[2], &pkdbuf[3], NULL);
+ }
+ assert (! ret);
+
+ printf ("%s ", identifier);
+
+ err = b64enc_start (&b64_state, stdout, "");
+ assert (! err);
+ err = b64enc_write (&b64_state, blob, blob_n);
+ assert (! err);
+ err = b64enc_finish (&b64_state);
+ assert (! err);
+
+ printf (" COMMENT\n");
+
+ return 0;
+}
diff --git a/tools/gpgparsemail.c b/tools/gpgparsemail.c
new file mode 100644
index 000000000..30759f9a4
--- /dev/null
+++ b/tools/gpgparsemail.c
@@ -0,0 +1,767 @@
+/* gpgparsemail.c - Standalone crypto mail parser
+ * 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.
+ */
+
+
+/* This utility prints an RFC8222, possible MIME structured, message
+ in an annotated format with the first column having an indicator
+ for the content of the line. Several options are available to
+ scrutinize the message. S/MIME and OpenPGP support is included. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+
+#include "rfc822parse.h"
+
+
+#define PGM "gpgparsemail"
+
+/* Option flags. */
+static int verbose;
+static int debug;
+static int opt_crypto; /* Decrypt or verify messages. */
+static int opt_no_header; /* Don't output the header lines. */
+
+/* Structure used to communicate with the parser callback. */
+struct parse_info_s {
+ int show_header; /* Show the header lines. */
+ int show_data; /* Show the data lines. */
+ unsigned int skip_show; /* Temporary disable above for these
+ number of lines. */
+ int show_data_as_note; /* The next data line should be shown
+ as a note. */
+ int show_boundary;
+ int nesting_level;
+
+ int is_pkcs7; /* Old style S/MIME message. */
+
+ int gpgsm_mime; /* gpgsm shall be used from S/MIME. */
+ char *signing_protocol;
+ int hashing_level; /* The nesting level we are hashing. */
+ int hashing;
+ FILE *hash_file;
+
+ FILE *sig_file; /* Signature part with MIME or full
+ pkcs7 data if IS_PCKS7 is set. */
+ int verify_now; /* Flag set when all signature data is
+ available. */
+};
+
+
+/* Print diagnostic message and exit with failure. */
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ putc ('\n', stderr);
+
+ exit (1);
+}
+
+
+/* Print diagnostic message. */
+static void
+err (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ putc ('\n', stderr);
+}
+
+static void *
+xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p)
+ die ("out of core: %s", strerror (errno));
+ return p;
+}
+
+/* static void * */
+/* xcalloc (size_t n, size_t m) */
+/* { */
+/* void *p = calloc (n, m); */
+/* if (!p) */
+/* die ("out of core: %s", strerror (errno)); */
+/* return p; */
+/* } */
+
+/* static void * */
+/* xrealloc (void *old, size_t n) */
+/* { */
+/* void *p = realloc (old, n); */
+/* if (!p) */
+/* die ("out of core: %s", strerror (errno)); */
+/* return p; */
+/* } */
+
+static char *
+xstrdup (const char *string)
+{
+ void *p = malloc (strlen (string)+1);
+ if (!p)
+ die ("out of core: %s", strerror (errno));
+ strcpy (p, string);
+ return p;
+}
+
+#ifndef HAVE_STPCPY
+static char *
+stpcpy (char *a,const char *b)
+{
+ while (*b)
+ *a++ = *b++;
+ *a = 0;
+
+ return (char*)a;
+}
+#endif
+
+static int
+run_gnupg (int smime, int sig_fd, int data_fd, int *close_list)
+{
+ int rp[2];
+ pid_t pid;
+ int i, c, is_status;
+ unsigned int pos;
+ char status_buf[10];
+ const char *cmd = smime? "gpgsm":"gpg";
+ FILE *fp;
+
+ if (pipe (rp) == -1)
+ die ("error creating a pipe: %s", strerror (errno));
+
+ pid = fork ();
+ if (pid == -1)
+ die ("error forking process: %s", strerror (errno));
+
+ if (!pid)
+ { /* Child. */
+ char data_fd_buf[50];
+ int fd;
+
+ /* Connect our signature fd to stdin. */
+ if (sig_fd != 0)
+ {
+ if (dup2 (sig_fd, 0) == -1)
+ die ("dup2 stdin failed: %s", strerror (errno));
+ }
+
+ /* Keep our data fd and format it for gpg/gpgsm use. */
+ if (data_fd == -1)
+ *data_fd_buf = 0;
+ else
+ sprintf (data_fd_buf, "-&%d", data_fd);
+
+ /* Send stdout to the bit bucket. */
+ fd = open ("/dev/null", O_WRONLY);
+ if (fd == -1)
+ die ("can't open `/dev/null': %s", strerror (errno));
+ if (fd != 1)
+ {
+ if (dup2 (fd, 1) == -1)
+ die ("dup2 stderr failed: %s", strerror (errno));
+ }
+
+ /* Connect stderr to our pipe. */
+ if (rp[1] != 2)
+ {
+ if (dup2 (rp[1], 2) == -1)
+ die ("dup2 stderr failed: %s", strerror (errno));
+ }
+
+ /* Close other files. */
+ for (i=0; (fd=close_list[i]) != -1; i++)
+ if (fd > 2 && fd != data_fd)
+ close (fd);
+ errno = 0;
+
+ execlp (cmd, cmd,
+ "--enable-special-filenames",
+ "--status-fd", "2",
+ "--assume-base64",
+ "--verify",
+ "--",
+ "-", data_fd == -1? NULL : data_fd_buf,
+ NULL);
+
+ die ("failed to exec the crypto command: %s", strerror (errno));
+ }
+
+ /* Parent. */
+ close (rp[1]);
+
+ fp = fdopen (rp[0], "r");
+ if (!fp)
+ die ("can't fdopen pipe for reading: %s", strerror (errno));
+
+ pos = 0;
+ is_status = 0;
+ assert (sizeof status_buf > 9);
+ while ((c=getc (fp)) != EOF)
+ {
+ if (pos < 9)
+ status_buf[pos] = c;
+ else
+ {
+ if (pos == 9)
+ {
+ is_status = !memcmp (status_buf, "[GNUPG:] ", 9);
+ if (is_status)
+ fputs ( "c ", stdout);
+ else if (verbose)
+ fputs ( "# ", stdout);
+ fwrite (status_buf, 9, 1, stdout);
+ }
+ putchar (c);
+ }
+ if (c == '\n')
+ {
+ if (verbose && pos < 9)
+ {
+ fputs ( "# ", stdout);
+ fwrite (status_buf, pos+1, 1, stdout);
+ }
+ pos = 0;
+ }
+ else
+ pos++;
+ }
+ if (pos)
+ {
+ if (verbose && pos < 9)
+ {
+ fputs ( "# ", stdout);
+ fwrite (status_buf, pos+1, 1, stdout);
+ }
+ putchar ('\n');
+ }
+ fclose (fp);
+
+ while ( (i=waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
+ ;
+ if (i == -1)
+ die ("waiting for child failed: %s", strerror (errno));
+
+ return 0;
+}
+
+
+
+
+/* Verify the signature in the current temp files. */
+static void
+verify_signature (struct parse_info_s *info)
+{
+ int close_list[10];
+
+ if (info->is_pkcs7)
+ {
+ assert (!info->hash_file);
+ assert (info->sig_file);
+ rewind (info->sig_file);
+ }
+ else
+ {
+ assert (info->hash_file);
+ assert (info->sig_file);
+ rewind (info->hash_file);
+ rewind (info->sig_file);
+ }
+
+/* printf ("# Begin hashed data\n"); */
+/* while ( (c=getc (info->hash_file)) != EOF) */
+/* putchar (c); */
+/* printf ("# End hashed data signature\n"); */
+/* printf ("# Begin signature\n"); */
+/* while ( (c=getc (info->sig_file)) != EOF) */
+/* putchar (c); */
+/* printf ("# End signature\n"); */
+/* rewind (info->hash_file); */
+/* rewind (info->sig_file); */
+
+ close_list[0] = -1;
+ run_gnupg (1, fileno (info->sig_file),
+ info->hash_file ? fileno (info->hash_file) : -1, close_list);
+}
+
+
+
+
+
+/* Prepare for a multipart/signed.
+ FIELD_CTX is the parsed context of the content-type header.*/
+static void
+mime_signed_begin (struct parse_info_s *info, rfc822parse_t msg,
+ rfc822parse_field_t field_ctx)
+{
+ const char *s;
+ s = rfc822parse_query_parameter (field_ctx, "protocol", 1);
+ if (s)
+ {
+ printf ("h signed.protocol: %s\n", s);
+ if (!strcmp (s, "application/pkcs7-signature")
+ || !strcmp (s, "application/x-pkcs7-signature"))
+ {
+ if (info->gpgsm_mime)
+ err ("note: ignoring nested pkcs7-signature");
+ else
+ {
+ info->gpgsm_mime = 1;
+ free (info->signing_protocol);
+ info->signing_protocol = xstrdup (s);
+ }
+ }
+ else if (verbose)
+ printf ("# this protocol is not supported\n");
+ }
+}
+
+
+/* Prepare for a multipart/encrypted.
+ FIELD_CTX is the parsed context of the content-type header.*/
+static void
+mime_encrypted_begin (struct parse_info_s *info, rfc822parse_t msg,
+ rfc822parse_field_t field_ctx)
+{
+ const char *s;
+ s = rfc822parse_query_parameter (field_ctx, "protocol", 0);
+ if (s)
+ printf ("h encrypted.protocol: %s\n", s);
+}
+
+
+/* Prepare for old-style pkcs7 messages. */
+static void
+pkcs7_begin (struct parse_info_s *info, rfc822parse_t msg,
+ rfc822parse_field_t field_ctx)
+{
+ const char *s;
+ s = rfc822parse_query_parameter (field_ctx, "name", 0);
+ if (s)
+ printf ("h pkcs7.name: %s\n", s);
+ if (info->is_pkcs7)
+ err ("note: ignoring nested pkcs7 data");
+ else
+ {
+ info->is_pkcs7 = 1;
+ if (opt_crypto)
+ {
+ assert (!info->sig_file);
+ info->sig_file = tmpfile ();
+ if (!info->sig_file)
+ die ("error creating temp file: %s", strerror (errno));
+ }
+ }
+}
+
+
+/* Print the event received by the parser for debugging as comment
+ line. */
+static void
+show_event (rfc822parse_event_t event)
+{
+ const char *s;
+
+ switch (event)
+ {
+ case RFC822PARSE_OPEN: s= "Open"; break;
+ case RFC822PARSE_CLOSE: s= "Close"; break;
+ case RFC822PARSE_CANCEL: s= "Cancel"; break;
+ case RFC822PARSE_T2BODY: s= "T2Body"; break;
+ case RFC822PARSE_FINISH: s= "Finish"; break;
+ case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
+ case RFC822PARSE_LEVEL_DOWN: s= "Level_Down"; break;
+ case RFC822PARSE_LEVEL_UP: s= "Level_Up"; break;
+ case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
+ case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
+ case RFC822PARSE_BEGIN_HEADER: s= "Begin_Header"; break;
+ case RFC822PARSE_PREAMBLE: s= "Preamble"; break;
+ case RFC822PARSE_EPILOGUE: s= "Epilogue"; break;
+ default: s= "[unknown event]"; break;
+ }
+ printf ("# *** got RFC822 event %s\n", s);
+}
+
+/* This function is called by the parser to communicate events. This
+ callback comminucates with the main program using a structure
+ passed in OPAQUE. Should retrun 0 or set errno and return -1. */
+static int
+message_cb (void *opaque, rfc822parse_event_t event, rfc822parse_t msg)
+{
+ struct parse_info_s *info = opaque;
+
+ if (debug)
+ show_event (event);
+ if (event == RFC822PARSE_OPEN)
+ {
+ /* Initialize for a new message. */
+ info->show_header = 1;
+ }
+ else if (event == RFC822PARSE_T2BODY)
+ {
+ rfc822parse_field_t ctx;
+
+ ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+ if (ctx)
+ {
+ const char *s1, *s2;
+ s1 = rfc822parse_query_media_type (ctx, &s2);
+ if (s1)
+ {
+ printf ("h media: %*s%s %s\n",
+ info->nesting_level*2, "", s1, s2);
+ if (info->gpgsm_mime == 3)
+ {
+ char *buf = xmalloc (strlen (s1) + strlen (s2) + 2);
+ strcpy (stpcpy (stpcpy (buf, s1), "/"), s2);
+ assert (info->signing_protocol);
+ if (strcmp (buf, info->signing_protocol))
+ err ("invalid S/MIME structure; expected `%s', found `%s'",
+ info->signing_protocol, buf);
+ else
+ {
+ printf ("c begin_signature\n");
+ info->gpgsm_mime++;
+ if (opt_crypto)
+ {
+ assert (!info->sig_file);
+ info->sig_file = tmpfile ();
+ if (!info->sig_file)
+ die ("error creating temp file: %s",
+ strerror (errno));
+ }
+ }
+ free (buf);
+ }
+ else if (!strcmp (s1, "multipart"))
+ {
+ if (!strcmp (s2, "signed"))
+ mime_signed_begin (info, msg, ctx);
+ else if (!strcmp (s2, "encrypted"))
+ mime_encrypted_begin (info, msg, ctx);
+ }
+ else if (!strcmp (s1, "application")
+ && (!strcmp (s2, "pkcs7-mime")
+ || !strcmp (s2, "x-pkcs7-mime")))
+ pkcs7_begin (info, msg, ctx);
+ }
+ else
+ printf ("h media: %*s none\n", info->nesting_level*2, "");
+
+ rfc822parse_release_field (ctx);
+ }
+ else
+ printf ("h media: %*stext plain [assumed]\n",
+ info->nesting_level*2, "");
+ info->show_header = 0;
+ info->show_data = 1;
+ info->skip_show = 1;
+ }
+ else if (event == RFC822PARSE_PREAMBLE)
+ info->show_data_as_note = 1;
+ else if (event == RFC822PARSE_LEVEL_DOWN)
+ {
+ printf ("b down\n");
+ info->nesting_level++;
+ }
+ else if (event == RFC822PARSE_LEVEL_UP)
+ {
+ printf ("b up\n");
+ if (info->nesting_level)
+ info->nesting_level--;
+ else
+ err ("invalid structure (bad nesting level)");
+ }
+ else if (event == RFC822PARSE_BOUNDARY || event == RFC822PARSE_LAST_BOUNDARY)
+ {
+ info->show_data = 0;
+ info->show_boundary = 1;
+ if (event == RFC822PARSE_BOUNDARY)
+ {
+ info->show_header = 1;
+ info->skip_show = 1;
+ printf ("b part\n");
+ }
+ else
+ printf ("b last\n");
+
+ if (info->gpgsm_mime == 2 && info->nesting_level == info->hashing_level)
+ {
+ printf ("c end_hash\n");
+ info->gpgsm_mime++;
+ info->hashing = 0;
+ }
+ else if (info->gpgsm_mime == 4)
+ {
+ printf ("c end_signature\n");
+ info->verify_now = 1;
+ }
+ }
+ else if (event == RFC822PARSE_BEGIN_HEADER)
+ {
+ if (info->gpgsm_mime == 1)
+ {
+ printf ("c begin_hash\n");
+ info->hashing = 1;
+ info->hashing_level = info->nesting_level;
+ info->gpgsm_mime++;
+
+ if (opt_crypto)
+ {
+ assert (!info->hash_file);
+ info->hash_file = tmpfile ();
+ if (!info->hash_file)
+ die ("failed to create temporary file: %s", strerror (errno));
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/* Read a message from FP and process it according to the global
+ options. */
+static void
+parse_message (FILE *fp)
+{
+ char line[5000];
+ size_t length;
+ rfc822parse_t msg;
+ unsigned int lineno = 0;
+ int no_cr_reported = 0;
+ struct parse_info_s info;
+
+ memset (&info, 0, sizeof info);
+
+ msg = rfc822parse_open (message_cb, &info);
+ if (!msg)
+ die ("can't open parser: %s", strerror (errno));
+
+ /* Fixme: We should not use fgets becuase it can't cope with
+ embedded nul characters. */
+ while (fgets (line, sizeof (line), fp))
+ {
+ lineno++;
+ if (lineno == 1 && !strncmp (line, "From ", 5))
+ continue; /* We better ignore a leading From line. */
+
+ length = strlen (line);
+ if (length && line[length - 1] == '\n')
+ line[--length] = 0;
+ else
+ err ("line number %u too long or last line not terminated", lineno);
+ if (length && line[length - 1] == '\r')
+ line[--length] = 0;
+ else if (verbose && !no_cr_reported)
+ {
+ err ("non canonical ended line detected (line %u)", lineno);
+ no_cr_reported = 1;
+ }
+
+
+ if (rfc822parse_insert (msg, line, length))
+ die ("parser failed: %s", strerror (errno));
+
+ if (info.hashing)
+ {
+ /* Delay hashing of the CR/LF because the last line ending
+ belongs to the next boundary. */
+ if (debug)
+ printf ("# hashing %s`%s'\n", info.hashing==2?"CR,LF+":"", line);
+ if (opt_crypto)
+ {
+ if (info.hashing == 2)
+ fputs ("\r\n", info.hash_file);
+ fputs (line, info.hash_file);
+ if (ferror (info.hash_file))
+ die ("error writing to temporary file: %s", strerror (errno));
+ }
+
+ info.hashing = 2;
+ }
+
+ if (info.sig_file && opt_crypto)
+ {
+ if (info.verify_now)
+ {
+ verify_signature (&info);
+ if (info.hash_file)
+ fclose (info.hash_file);
+ info.hash_file = NULL;
+ fclose (info.sig_file);
+ info.sig_file = NULL;
+ info.gpgsm_mime = 0;
+ info.is_pkcs7 = 0;
+ }
+ else
+ {
+ fputs (line, info.sig_file);
+ fputs ("\r\n", info.sig_file);
+ if (ferror (info.sig_file))
+ die ("error writing to temporary file: %s", strerror (errno));
+ }
+ }
+
+ if (info.show_boundary)
+ {
+ if (!opt_no_header)
+ printf (":%s\n", line);
+ info.show_boundary = 0;
+ }
+
+ if (info.skip_show)
+ info.skip_show--;
+ else if (info.show_data)
+ {
+ if (info.show_data_as_note)
+ {
+ if (verbose)
+ printf ("# DATA: %s\n", line);
+ info.show_data_as_note = 0;
+ }
+ else
+ printf (" %s\n", line);
+ }
+ else if (info.show_header && !opt_no_header)
+ printf (".%s\n", line);
+
+ }
+
+ if (info.sig_file && opt_crypto && info.is_pkcs7)
+ {
+ verify_signature (&info);
+ fclose (info.sig_file);
+ info.sig_file = NULL;
+ info.is_pkcs7 = 0;
+ }
+
+ rfc822parse_close (msg);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+
+ if (argc)
+ {
+ argc--; argv++;
+ }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--help"))
+ {
+ puts (
+ "Usage: " PGM " [OPTION] [FILE]\n"
+ "Parse a mail message into an annotated format.\n\n"
+ " --crypto decrypt or verify messages\n"
+ " --no-header don't output the header lines\n"
+ " --verbose enable extra informational output\n"
+ " --debug enable additional debug output\n"
+ " --help display this help and exit\n\n"
+ "With no FILE, or when FILE is -, read standard input.\n\n"
+ "WARNING: This tool is under development.\n"
+ " The semantics may change without notice\n\n"
+ "Report bugs to <[email protected]>.");
+ exit (0);
+ }
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--debug"))
+ {
+ verbose = debug = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--crypto"))
+ {
+ opt_crypto = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--no-header"))
+ {
+ opt_no_header = 1;
+ argc--; argv++;
+ }
+ }
+
+ if (argc > 1)
+ die ("usage: " PGM " [OPTION] [FILE] (try --help for more information)\n");
+
+ signal (SIGPIPE, SIG_IGN);
+
+ if (argc && strcmp (*argv, "-"))
+ {
+ FILE *fp = fopen (*argv, "rb");
+ if (!fp)
+ die ("can't open `%s': %s", *argv, strerror (errno));
+ parse_message (fp);
+ fclose (fp);
+ }
+ else
+ parse_message (stdin);
+
+ return 0;
+}
+
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -o gpgparsemail rfc822parse.c gpgparsemail.c"
+End:
+*/
diff --git a/tools/gpgsm-gencert.sh b/tools/gpgsm-gencert.sh
new file mode 100755
index 000000000..3949f2361
--- /dev/null
+++ b/tools/gpgsm-gencert.sh
@@ -0,0 +1,171 @@
+#!/bin/sh
+# -*- sh -*-
+# gpgsm-gencert.c - Generate X.509 certificates through GPGSM.
+# Copyright (C) 2004, 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., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+set -e
+
+ASSUAN_FP_IN=4
+ASSUAN_FP_OUT=5
+
+ASSUAN_COMMANDS="\
+INPUT FD=$ASSUAN_FP_IN\n\
+OUTPUT FD=$ASSUAN_FP_OUT --armor\n\
+GENKEY\n\
+BYE"
+
+ANSWER=""
+
+query_user()
+{
+ message=$1; shift
+
+ echo "$message" >&2
+ echo -n "> " >&2
+ read answer
+
+ ANSWER=$answer;
+}
+
+query_user_menu()
+{
+ message=$1; shift
+ i=0
+
+ echo "$message" >&2
+ for choice in "$@"; do
+ i=$(expr $i + 1)
+ echo " [$i] $choice" >&2
+ done
+
+ while true; do
+ j=1
+ echo -n "Your selection: " >&2
+ read idx
+
+ while [ $j -lt $i -o $j -eq $i ]; do
+ if [ "$idx" = $j ]; then
+ break
+ fi
+ j=$(expr $j + 1)
+ done
+ if [ $j -lt $i -o $j -eq $i ]; then
+ break
+ fi
+ done
+
+ i=0
+ for choice in "$@"; do
+ i=$(expr $i + 1)
+ if [ $i -eq $idx ]; then
+ ANSWER=$1
+ break;
+ fi
+ shift
+ done
+
+ echo "You selected: $ANSWER" >&2
+}
+
+query_user_menu "Key type" "RSA" "existing key"
+if [ "$ANSWER" = "existing key" ]; then
+ # User requested to use an existing key; need to set some dummy defaults
+ KEY_TYPE=RSA
+ KEY_LENGTH=1024
+ query_user "Keygrip "
+ KEY_GRIP=$ANSWER
+else
+ KEY_TYPE=$ANSWER
+ query_user_menu "Key length" "1024" "2048"
+ KEY_LENGTH=$ANSWER
+ KEY_GRIP=
+fi
+
+
+query_user_menu "Key usage" "sign, encrypt" "sign" "encrypt"
+KEY_USAGE=$ANSWER
+
+query_user "Name (DN)"
+NAME=$ANSWER
+
+EMAIL_ADDRESSES=
+LF=
+while : ; do
+ query_user "E-Mail addresses (end with an empty line)"
+ [ -z "$ANSWER" ] && break
+ EMAIL_ADDRESSES="${EMAIL_ADDRESSES}${LF}Name-Email: $ANSWER"
+ LF='
+'
+done
+
+DNS_ADDRESSES=
+LF=
+while : ; do
+ query_user "DNS Names (optional; end with an empty line)"
+ [ -z "$ANSWER" ] && break
+ DNS_ADDRESSES="${DNS_ADDRESSES}${LF}Name-DNS: $ANSWER"
+ LF='
+'
+done
+
+URI_ADDRESSES=
+LF=
+while : ; do
+ query_user "URIs (optional; end with an empty line)"
+ [ -z "$ANSWER" ] && break
+ URI_ADDRESSES="${URI_ADDRESSES}${LF}Name-URI: $ANSWER"
+ LF='
+'
+done
+
+file_parameter=$(mktemp "/tmp/gpgsm.XXXXXX")
+outfile=$(mktemp "/tmp/gpgsm.XXXXXX")
+
+
+(
+cat <<EOF
+Key-Type: $KEY_TYPE
+Key-Length: $KEY_LENGTH
+Key-Usage: $KEY_USAGE
+Name-DN: $NAME
+EOF
+[ -n "$KEY_GRIP" ] && echo "Key-Grip: $KEY_GRIP"
+[ -n "$EMAIL_ADDRESSES" ] && echo "$EMAIL_ADDRESSES"
+[ -n "$DNS_ADDRESSES" ] && echo "$DNS_ADDRESSES"
+[ -n "$URI_ADDRESSES" ] && echo "$URI_ADDRESSES"
+) > "$file_parameter"
+
+
+echo 'Parameters for certificate request to create:' >&2
+cat -n "$file_parameter" >&2
+echo >&2
+
+query_user_menu "Really create such a CSR?" "yes" "no"
+[ "$ANSWER" != "yes" ] && exit 1
+
+
+echo -e "$ASSUAN_COMMANDS" | \
+ gpgsm --no-log-file --debug-level none --debug-none \
+ --server 4< "$file_parameter" 5>"$outfile" >/dev/null
+
+cat "$outfile"
+
+rm "$file_parameter" "$outfile"
+exit 0
diff --git a/tools/gpgsplit.c b/tools/gpgsplit.c
deleted file mode 100644
index 3cdae05bf..000000000
--- a/tools/gpgsplit.c
+++ /dev/null
@@ -1,879 +0,0 @@
-/* gpgsplit.c - An OpenPGP packet splitting tool
- * Copyright (C) 2001, 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.
- */
-
-/*
- * TODO: Add an option to uncompress packets. This should come quite handy.
- */
-
-#include <config.h>
-#include <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
-#include <assert.h>
-#ifdef HAVE_DOSISH_SYSTEM
-# include <fcntl.h> /* for setmode() */
-#endif
-#include <zlib.h>
-#ifdef HAVE_BZIP2
-#include <bzlib.h>
-#endif /* HAVE_BZIP2 */
-#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
-# include "zlib-riscos.h"
-#endif
-
-#define INCLUDED_BY_MAIN_MODULE 1
-#include "../g10/packet.h"
-#include "util.h"
-
-static int opt_verbose;
-static const char *opt_prefix = "";
-static int opt_uncompress;
-static int opt_secret_to_public;
-static int opt_no_split;
-
-static void g10_exit( int rc );
-static void split_packets (const char *fname);
-
-
-enum cmd_and_opt_values {
- aNull = 0,
- oVerbose = 'v',
- oPrefix = 'p',
- oUncompress = 500,
- oSecretToPublic,
- oNoSplit,
-
- aTest
-};
-
-
-static ARGPARSE_OPTS opts[] = {
-
- { 301, NULL, 0, "@Options:\n " },
-
- { oVerbose, "verbose", 0, "verbose" },
- { oPrefix, "prefix", 2, "|STRING|Prepend filenames with STRING" },
- { oUncompress, "uncompress", 0, "uncompress a packet"},
- { oSecretToPublic, "secret-to-public", 0, "convert secret keys to public keys"},
- { oNoSplit, "no-split", 0, "write to stdout and don't actually split"},
-{0} };
-
-
-const char *
-strusage( int level )
-{
- const char *p;
- switch (level)
- {
- case 11: p = "gpgsplit (GnuPG)";
- break;
- case 13: p = VERSION; break;
- case 17: p = PRINTABLE_OS_NAME; break;
- case 19: p =
- "Please report bugs to <[email protected]>.\n";
- break;
- case 1:
- case 40: p =
- "Usage: gpgsplit [options] [files] (-h for help)";
- break;
- case 41: p =
- "Syntax: gpgsplit [options] [files]\n"
- "Split an OpenPGP message into packets\n";
- break;
-
- default: p = default_strusage(level);
- }
- return p;
-}
-
-
-
-int
-main( int argc, char **argv )
-{
- ARGPARSE_ARGS pargs;
-
-#ifdef HAVE_DOSISH_SYSTEM
- setmode( fileno(stdin), O_BINARY );
- setmode( fileno(stdout), O_BINARY );
-#endif
- log_set_name("gpgsplit");
-
- pargs.argc = &argc;
- pargs.argv = &argv;
- pargs.flags= 1; /* do not remove the args */
- while (optfile_parse( NULL, NULL, NULL, &pargs, opts))
- {
- switch (pargs.r_opt)
- {
- case oVerbose: opt_verbose = 1; break;
- case oPrefix: opt_prefix = pargs.r.ret_str; break;
- case oUncompress: opt_uncompress = 1; break;
- case oSecretToPublic: opt_secret_to_public = 1; break;
- case oNoSplit: opt_no_split = 1; break;
- default : pargs.err = 2; break;
- }
- }
-
- if (log_get_errorcount(0))
- g10_exit (2);
-
- if (!argc)
- split_packets (NULL);
- else
- {
- for ( ;argc; argc--, argv++)
- split_packets (*argv);
- }
-
- g10_exit (0);
- return 0;
-}
-
-
-static void
-g10_exit (int rc)
-{
- rc = rc? rc : log_get_errorcount(0)? 2 : 0;
- exit(rc );
-}
-
-static const char *
-pkttype_to_string (int pkttype)
-{
- const char *s;
-
- switch (pkttype)
- {
- case PKT_PUBKEY_ENC : s = "pk_enc"; break;
- case PKT_SIGNATURE : s = "sig"; break;
- case PKT_SYMKEY_ENC : s = "sym_enc"; break;
- case PKT_ONEPASS_SIG : s = "onepass_sig"; break;
- case PKT_SECRET_KEY : s = "secret_key"; break;
- case PKT_PUBLIC_KEY : s = "public_key"; break;
- case PKT_SECRET_SUBKEY : s = "secret_subkey"; break;
- case PKT_COMPRESSED :
- s = opt_uncompress? "uncompressed":"compressed";
- break;
- case PKT_ENCRYPTED : s = "encrypted"; break;
- case PKT_MARKER : s = "marker"; break;
- case PKT_PLAINTEXT : s = "plaintext"; break;
- case PKT_RING_TRUST : s = "ring_trust"; break;
- case PKT_USER_ID : s = "user_id"; break;
- case PKT_PUBLIC_SUBKEY : s = "public_subkey"; break;
- case PKT_OLD_COMMENT : s = "old_comment"; break;
- case PKT_ATTRIBUTE : s = "attribute"; break;
- case PKT_ENCRYPTED_MDC : s = "encrypted_mdc"; break;
- case PKT_MDC : s = "mdc"; break;
- case PKT_COMMENT : s = "comment"; break;
- case PKT_GPG_CONTROL : s = "gpg_control"; break;
- default: s = "unknown"; break;
- }
- return s;
-}
-
-
-/*
- * Create a new filename and a return a pointer to a statically
- * allocated buffer
- */
-static char *
-create_filename (int pkttype)
-{
- static unsigned int partno = 0;
- static char *name;
-
- if (!name)
- name = xmalloc (strlen (opt_prefix) + 100 );
-
- assert (pkttype < 1000 && pkttype >= 0 );
- partno++;
- sprintf (name, "%s%06u-%03d" EXTSEP_S "%.40s",
- opt_prefix, partno, pkttype, pkttype_to_string (pkttype));
- return name;
-}
-
-static int
-read_u16 (FILE *fp, size_t *rn)
-{
- int c;
-
- if ( (c = getc (fp)) == EOF )
- return -1;
- *rn = c << 8;
- if ( (c = getc (fp)) == EOF )
- return -1;
- *rn |= c;
- return 0;
-}
-
-static int
-read_u32 (FILE *fp, unsigned long *rn)
-{
- size_t tmp;
-
- if (read_u16 (fp, &tmp))
- return -1;
- *rn = tmp << 16;
- if (read_u16 (fp, &tmp))
- return -1;
- *rn |= tmp;
- return 0;
-}
-
-static int
-write_old_header (FILE *fp, int pkttype, unsigned int len)
-{
- int ctb = (0x80 | ((pkttype & 15)<<2));
-
- if (len < 256)
- ;
- else if (len < 65536)
- ctb |= 1;
- else
- ctb |= 2;
-
- if ( putc ( ctb, fp) == EOF )
- return -1;
-
- if ( (ctb & 2) )
- {
- if (putc ((len>>24), fp) == EOF)
- return -1;
- if (putc ((len>>16), fp) == EOF)
- return -1;
- }
- if ( (ctb & 3) )
- {
- if (putc ((len>>8), fp) == EOF)
- return -1;
- }
- if (putc ((len&0xff), fp) == EOF)
- return -1;
- return 0;
-}
-
-static int
-write_new_header (FILE *fp, int pkttype, unsigned int len)
-{
- if ( putc ((0xc0 | (pkttype & 0x3f)), fp) == EOF )
- return -1;
-
- if (len < 192)
- {
- if (putc (len, fp) == EOF)
- return -1;
- }
- else if (len < 8384)
- {
- len -= 192;
- if (putc ((len/256)+192, fp) == EOF)
- return -1;
- if (putc ((len%256), fp) == EOF)
- return -1;
- }
- else
- {
- if (putc ( 0xff, fp) == EOF)
- return -1;
- if (putc ( (len >> 24), fp) == EOF)
- return -1;
- if (putc ( (len >> 16), fp) == EOF)
- return -1;
- if (putc ( (len >> 8), fp) == EOF)
- return -1;
- if (putc ( (len & 0xff), fp) == EOF)
- return -1;
- }
- return 0;
-}
-
-/* Return the length of the public key given BUF of BUFLEN with a
- secret key. */
-static int
-public_key_length (const unsigned char *buf, size_t buflen)
-{
- const unsigned char *s;
- int nmpis;
-
- /* byte version number (3 or 4)
- u32 creation time
- [u16 valid days (version 3 only)]
- byte algorithm
- n MPIs (n and e) */
- if (!buflen)
- return 0;
- if (buf[0] < 2 || buf[0] > 4)
- return 0; /* wrong version number */
- if (buflen < (buf[0] == 4? 6:8))
- return 0;
- s = buf + (buf[0] == 4? 6:8);
- buflen -= (buf[0] == 4? 6:8);
- switch (s[-1])
- {
- case 1:
- case 2:
- case 3:
- nmpis = 2;
- break;
- case 16:
- case 20:
- nmpis = 3;
- break;
- case 17:
- nmpis = 4;
- break;
- default:
- return 0;
- }
-
- for (; nmpis; nmpis--)
- {
- unsigned int nbits, nbytes;
-
- if (buflen < 2)
- return 0;
- nbits = (s[0] << 8) | s[1];
- s += 2; buflen -= 2;
- nbytes = (nbits+7) / 8;
- if (buflen < nbytes)
- return 0;
- s += nbytes; buflen -= nbytes;
- }
-
- return s - buf;
-}
-
-static int
-handle_zlib(int algo,FILE *fpin,FILE *fpout)
-{
- z_stream zs;
- byte *inbuf, *outbuf;
- unsigned int inbufsize, outbufsize;
- int c,zinit_done, zrc, nread, count;
- size_t n;
-
- memset (&zs, 0, sizeof zs);
- inbufsize = 2048;
- inbuf = xmalloc (inbufsize);
- outbufsize = 8192;
- outbuf = xmalloc (outbufsize);
- zs.avail_in = 0;
- zinit_done = 0;
-
- do
- {
- if (zs.avail_in < inbufsize)
- {
- n = zs.avail_in;
- if (!n)
- zs.next_in = (Bytef *) inbuf;
- count = inbufsize - n;
- for (nread=0;
- nread < count && (c=getc (fpin)) != EOF;
- nread++)
- inbuf[n+nread] = c;
-
- n += nread;
- if (nread < count && algo == 1)
- {
- inbuf[n] = 0xFF; /* chew dummy byte */
- n++;
- }
- zs.avail_in = n;
- }
- zs.next_out = (Bytef *) outbuf;
- zs.avail_out = outbufsize;
-
- if (!zinit_done)
- {
- zrc = (algo == 1? inflateInit2 ( &zs, -13)
- : inflateInit ( &zs ));
- if (zrc != Z_OK)
- {
- log_fatal ("zlib problem: %s\n", zs.msg? zs.msg :
- zrc == Z_MEM_ERROR ? "out of core" :
- zrc == Z_VERSION_ERROR ?
- "invalid lib version" :
- "unknown error" );
- }
- zinit_done = 1;
- }
- else
- {
-#ifdef Z_SYNC_FLUSH
- zrc = inflate (&zs, Z_SYNC_FLUSH);
-#else
- zrc = inflate (&zs, Z_PARTIAL_FLUSH);
-#endif
- if (zrc == Z_STREAM_END)
- ; /* eof */
- else if (zrc != Z_OK && zrc != Z_BUF_ERROR)
- {
- if (zs.msg)
- log_fatal ("zlib inflate problem: %s\n", zs.msg );
- else
- log_fatal ("zlib inflate problem: rc=%d\n", zrc );
- }
- for (n=0; n < outbufsize - zs.avail_out; n++)
- {
- if (putc (outbuf[n], fpout) == EOF )
- return 1;
- }
- }
- }
- while (zrc != Z_STREAM_END && zrc != Z_BUF_ERROR);
- inflateEnd (&zs);
-
- return 0;
-}
-
-#ifdef HAVE_BZIP2
-static int
-handle_bzip2(int algo,FILE *fpin,FILE *fpout)
-{
- bz_stream bzs;
- byte *inbuf, *outbuf;
- unsigned int inbufsize, outbufsize;
- int c,zinit_done, zrc, nread, count;
- size_t n;
-
- memset (&bzs, 0, sizeof bzs);
- inbufsize = 2048;
- inbuf = xmalloc (inbufsize);
- outbufsize = 8192;
- outbuf = xmalloc (outbufsize);
- bzs.avail_in = 0;
- zinit_done = 0;
-
- do
- {
- if (bzs.avail_in < inbufsize)
- {
- n = bzs.avail_in;
- if (!n)
- bzs.next_in = inbuf;
- count = inbufsize - n;
- for (nread=0;
- nread < count && (c=getc (fpin)) != EOF;
- nread++)
- inbuf[n+nread] = c;
-
- n += nread;
- if (nread < count && algo == 1)
- {
- inbuf[n] = 0xFF; /* chew dummy byte */
- n++;
- }
- bzs.avail_in = n;
- }
- bzs.next_out = outbuf;
- bzs.avail_out = outbufsize;
-
- if (!zinit_done)
- {
- zrc = BZ2_bzDecompressInit(&bzs,0,0);
- if (zrc != BZ_OK)
- log_fatal ("bz2lib problem: %d\n",zrc);
- zinit_done = 1;
- }
- else
- {
- zrc = BZ2_bzDecompress(&bzs);
- if (zrc == BZ_STREAM_END)
- ; /* eof */
- else if (zrc != BZ_OK && zrc != BZ_PARAM_ERROR)
- log_fatal ("bz2lib inflate problem: %d\n", zrc );
- for (n=0; n < outbufsize - bzs.avail_out; n++)
- {
- if (putc (outbuf[n], fpout) == EOF )
- return 1;
- }
- }
- }
- while (zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR);
- BZ2_bzDecompressEnd(&bzs);
-
- return 0;
-}
-#endif /* HAVE_BZIP2 */
-
-/* hdr must point to a buffer large enough to hold all header bytes */
-static int
-write_part ( const char *fname, FILE *fpin, unsigned long pktlen,
- int pkttype, int partial, unsigned char *hdr, size_t hdrlen)
-{
- FILE *fpout;
- int c, first;
- unsigned char *p;
- const char *outname = create_filename (pkttype);
-
-#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
- static int initialized = 0;
-
- if (!initialized)
- initialized = riscos_load_module("ZLib", zlib_path, 1);
-#endif
- if (opt_no_split)
- fpout = stdout;
- else
- {
- if (opt_verbose)
- log_info ("writing `%s'\n", outname);
- fpout = fopen (outname, "wb");
- if (!fpout)
- {
- log_error ("error creating `%s': %s\n", outname, strerror(errno));
- /* stop right now, otherwise we would mess up the sequence
- of the part numbers */
- g10_exit (1);
- }
- }
-
- if (opt_secret_to_public
- && (pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY))
- {
- unsigned char *blob = xmalloc (pktlen);
- int i, len;
-
- pkttype = pkttype == PKT_SECRET_KEY? PKT_PUBLIC_KEY:PKT_PUBLIC_SUBKEY;
-
- for (i=0; i < pktlen; i++)
- {
- c = getc (fpin);
- if (c == EOF)
- goto read_error;
- blob[i] = c;
- }
- len = public_key_length (blob, pktlen);
- if (!len)
- {
- log_error ("error calcualting public key length\n");
- g10_exit (1);
- }
- if ( (hdr[0] & 0x40) )
- {
- if (write_new_header (fpout, pkttype, len))
- goto write_error;
- }
- else
- {
- if (write_old_header (fpout, pkttype, len))
- goto write_error;
- }
-
- for (i=0; i < len; i++)
- {
- if ( putc (blob[i], fpout) == EOF )
- goto write_error;
- }
-
- goto ready;
- }
-
-
- if (!opt_uncompress)
- {
- for (p=hdr; hdrlen; p++, hdrlen--)
- {
- if ( putc (*p, fpout) == EOF )
- goto write_error;
- }
- }
-
- first = 1;
- while (partial)
- {
- size_t partlen;
-
- if (partial == 1)
- { /* openpgp */
- if (first )
- {
- c = pktlen;
- assert( c >= 224 && c < 255 );
- first = 0;
- }
- else if ((c = getc (fpin)) == EOF )
- goto read_error;
- else
- hdr[hdrlen++] = c;
-
- if (c < 192)
- {
- pktlen = c;
- partial = 0; /* (last segment may follow) */
- }
- else if (c < 224 )
- {
- pktlen = (c - 192) * 256;
- if ((c = getc (fpin)) == EOF)
- goto read_error;
- hdr[hdrlen++] = c;
- pktlen += c + 192;
- partial = 0;
- }
- else if (c == 255)
- {
- if (read_u32 (fpin, &pktlen))
- goto read_error;
- hdr[hdrlen++] = pktlen >> 24;
- hdr[hdrlen++] = pktlen >> 16;
- hdr[hdrlen++] = pktlen >> 8;
- hdr[hdrlen++] = pktlen;
- partial = 0;
- }
- else
- { /* next partial body length */
- for (p=hdr; hdrlen; p++, hdrlen--)
- {
- if ( putc (*p, fpout) == EOF )
- goto write_error;
- }
- partlen = 1 << (c & 0x1f);
- for (; partlen; partlen--)
- {
- if ((c = getc (fpin)) == EOF)
- goto read_error;
- if ( putc (c, fpout) == EOF )
- goto write_error;
- }
- }
- }
- else if (partial == 2)
- { /* old gnupg */
- assert (!pktlen);
- if ( read_u16 (fpin, &partlen) )
- goto read_error;
- hdr[hdrlen++] = partlen >> 8;
- hdr[hdrlen++] = partlen;
- for (p=hdr; hdrlen; p++, hdrlen--)
- {
- if ( putc (*p, fpout) == EOF )
- goto write_error;
- }
- if (!partlen)
- partial = 0; /* end of packet */
- for (; partlen; partlen--)
- {
- c = getc (fpin);
- if (c == EOF)
- goto read_error;
- if ( putc (c, fpout) == EOF )
- goto write_error;
- }
- }
- else
- { /* compressed: read to end */
- pktlen = 0;
- partial = 0;
- hdrlen = 0;
- if (opt_uncompress)
- {
- if ((c = getc (fpin)) == EOF)
- goto read_error;
-
- if(c==1 || c==2)
- {
- if(handle_zlib(c,fpin,fpout))
- goto write_error;
- }
-#ifdef HAVE_BZIP2
- else if(c==3)
- {
- if(handle_bzip2(c,fpin,fpout))
- goto write_error;
- }
-#endif /* HAVE_BZIP2 */
- else
- {
- log_error("invalid compression algorithm (%d)\n",c);
- goto read_error;
- }
- }
- else
- {
- while ( (c=getc (fpin)) != EOF )
- {
- if ( putc (c, fpout) == EOF )
- goto write_error;
- }
- }
- if (!feof (fpin))
- goto read_error;
- }
- }
-
- for (p=hdr; hdrlen; p++, hdrlen--)
- {
- if ( putc (*p, fpout) == EOF )
- goto write_error;
- }
-
- /* standard packet or last segment of partial length encoded packet */
- for (; pktlen; pktlen--)
- {
- c = getc (fpin);
- if (c == EOF)
- goto read_error;
- if ( putc (c, fpout) == EOF )
- goto write_error;
- }
-
- ready:
- if ( !opt_no_split && fclose (fpout) )
- log_error ("error closing `%s': %s\n", outname, strerror (errno));
- return 0;
-
- write_error:
- log_error ("error writing `%s': %s\n", outname, strerror (errno));
- if (!opt_no_split)
- fclose (fpout);
- return 2;
-
- read_error:
- if (!opt_no_split)
- {
- int save = errno;
- fclose (fpout);
- errno = save;
- }
- return -1;
-}
-
-
-
-static int
-do_split (const char *fname, FILE *fp)
-{
- int c, ctb, pkttype;
- unsigned long pktlen = 0;
- int partial = 0;
- unsigned char header[20];
- int header_idx = 0;
-
- ctb = getc (fp);
- if (ctb == EOF)
- return 3; /* ready */
- header[header_idx++] = ctb;
-
- if (!(ctb & 0x80))
- {
- log_error("invalid CTB %02x\n", ctb );
- return 1;
- }
- if ( (ctb & 0x40) )
- { /* new CTB */
- pkttype = (ctb & 0x3f);
- if( (c = getc (fp)) == EOF )
- return -1;
- header[header_idx++] = c;
-
- if ( c < 192 )
- pktlen = c;
- else if ( c < 224 )
- {
- pktlen = (c - 192) * 256;
- if( (c = getc (fp)) == EOF )
- return -1;
- header[header_idx++] = c;
- pktlen += c + 192;
- }
- else if ( c == 255 )
- {
- if (read_u32 (fp, &pktlen))
- return -1;
- header[header_idx++] = pktlen >> 24;
- header[header_idx++] = pktlen >> 16;
- header[header_idx++] = pktlen >> 8;
- header[header_idx++] = pktlen;
- }
- else
- { /* partial body length */
- pktlen = c;
- partial = 1;
- }
- }
- else
- {
- int lenbytes;
-
- pkttype = (ctb>>2)&0xf;
- lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
- if (!lenbytes )
- {
- pktlen = 0; /* don't know the value */
- if( pkttype == PKT_COMPRESSED )
- partial = 3;
- else
- partial = 2; /* the old GnuPG partial length encoding */
- }
- else
- {
- for ( ; lenbytes; lenbytes-- )
- {
- pktlen <<= 8;
- if( (c = getc (fp)) == EOF )
- return -1;
- header[header_idx++] = c;
-
- pktlen |= c;
- }
- }
- }
-
- return write_part (fname, fp, pktlen, pkttype, partial,
- header, header_idx);
-}
-
-
-static void
-split_packets (const char *fname)
-{
- FILE *fp;
- int rc;
-
- if (!fname || !strcmp (fname, "-"))
- {
- fp = stdin;
- fname = "-";
- }
- else if ( !(fp = fopen (fname,"rb")) )
- {
- log_error ("can't open `%s': %s\n", fname, strerror (errno));
- return;
- }
-
- while ( !(rc = do_split (fname, fp)) )
- ;
- if ( rc > 0 )
- ; /* error already handled */
- else if ( ferror (fp) )
- log_error ("error reading `%s': %s\n", fname, strerror (errno));
- else
- log_error ("premature EOF while reading `%s'\n", fname );
-
- if ( fp != stdin )
- fclose (fp);
-}
diff --git a/tools/lspgpot b/tools/lspgpot
deleted file mode 100755
index f406392eb..000000000
--- a/tools/lspgpot
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-# lspgpot - script to extract the ownertrust values
-# from PGP keyrings and list them in GnuPG ownertrust format.
-#
-# 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 program 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.
-
-if ! gpg --version > /dev/null 2>&1 ; then
- echo "GnuPG not available!"
- exit 1
-fi
-
-gpg --dry-run --with-fingerprint --with-colons $* | awk '
-BEGIN { FS=":"
- printf "# Ownertrust listing generated by lspgpot\n"
- printf "# This can be imported using the command:\n"
- printf "# gpg --import-ownertrust\n\n" }
-$1 == "fpr" { fpr = $10 }
-$1 == "rtv" && $2 == 1 && $3 == 2 { printf "%s:3:\n", fpr; next }
-$1 == "rtv" && $2 == 1 && $3 == 5 { printf "%s:4:\n", fpr; next }
-$1 == "rtv" && $2 == 1 && $3 == 6 { printf "%s:5:\n", fpr; next }
-'
diff --git a/tools/mail-signed-keys b/tools/mail-signed-keys
deleted file mode 100755
index 80fbb3481..000000000
--- a/tools/mail-signed-keys
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/bin/sh
-# Copyright (C) 2000, 2001 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 program 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.
-
-# FIXME: Add --dry-run, use only valid email addreses, extract only given keys
-
-dryrun=0
-if [ "$1" = "--dry-run" ]; then
- dryrun=1
- shift
-fi
-
-if [ -z "$1" -o -z "$2" -o -z "$3" ]; then
- echo "usage: mail-signed-keys keyring signedby signame" >&2
- exit 1
-fi
-
-signame="$3"
-
-if [ ! -f $1 ]; then
- echo "mail-signed-keys: '$1': no such file" >&2
- exit 1
-fi
-
-[ -f '.#tdb.tmp' ] && rm '.#tdb.tmp'
-ro="--homedir . --no-options --trustdb-name=./.#tdb.tmp --dry-run --lock-never --no-default-keyring --keyring $1"
-
-signedby=`gpg $ro --list-keys --with-colons $2 \
- 2>/dev/null | awk -F: '$1=="pub" {print $5; exit 0}'`
-
-if [ -z "$signedby" ]; then
- echo "mail-signed-keys: '$2': no such signator" >&2
- exit 1
-fi
-
-if [ "$dryrun" = "0" ]; then
- echo "About to send the the keys signed by $signedby" >&2
- echo -n "to their owners. Do you really want to do this? (y/N)" >&2
- read
- [ "$REPLY" != "y" -a "$REPLY" != "Y" ] && exit 0
-fi
-
-gpg $ro --check-sigs --with-colons 2>/dev/null \
- | awk -F: -v signedby="$signedby" -v gpgopt="$ro" \
- -v dryrun="$dryrun" -v signame="$signame" '
-BEGIN { sendmail="/usr/lib/sendmail -oi -t " }
-$1 == "pub" { nextkid=$5; nextuid=$10
- if( uidcount > 0 ) { myflush() }
- kid=nextkid; uid=nextuid; next
- }
-$1 == "uid" { uid=$10 ; next }
-$1 == "sig" && $2 == "!" && $5 == signedby { uids[uidcount++] = uid; next }
-END { if( uidcount > 0 ) { myflush() } }
-
-function myflush()
-{
- if ( kid == signedby ) { uidcount=0; return }
- print "sending key " substr(kid,9) " to" | "cat >&2"
- for(i=0; i < 1; i++ ) {
- print " " uids[i] | "cat >&2"
- if( dryrun == 0 ) {
- if( i == 0 ) {
- printf "To: %s", uids[i] | sendmail
- }
- else {
- printf ",\n %s", uids[i] | sendmail
- }
- }
- }
- if(dryrun == 0) {
- printf "\n" | sendmail
- print "Subject: I signed your key " substr(kid,9) | sendmail
- print "" | sendmail
- print "Hi," | sendmail
- print "" | sendmail
- print "Here you get back the signed key." | sendmail
- print "Please send it yourself to a keyserver." | sendmail
- print "" | sendmail
- print "Peace," | sendmail
- print " " signame | sendmail
- print "" | sendmail
- cmd = "gpg " gpgopt " --export -a " kid " 2>/dev/null"
- while( (cmd | getline) > 0 ) {
- print | sendmail
- }
- print "" | sendmail
- close(cmd)
- close( sendmail )
- }
- uidcount=0
-}
-'
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/tools/make-dns-cert.c b/tools/make-dns-cert.c
deleted file mode 100644
index 496b63b0b..000000000
--- a/tools/make-dns-cert.c
+++ /dev/null
@@ -1,239 +0,0 @@
-/* make-dns-cert.c - An OpenPGP-to-DNS CERT conversion tool
- * 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.
- */
-
-#include <config.h>
-#include <unistd.h>
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-
-/* We use TYPE37 instead of CERT since not all nameservers can handle
- CERT yet... */
-
-static int
-cert_key(const char *name,const char *keyfile)
-{
- int fd,ret=1,err,i;
- struct stat statbuf;
-
- fd=open(keyfile,O_RDONLY);
- if(fd==-1)
- {
- fprintf(stderr,"Cannot open key file %s: %s\n",keyfile,strerror(errno));
- return 1;
- }
-
- err=fstat(fd,&statbuf);
- if(err==-1)
- {
- fprintf(stderr,"Unable to stat key file %s: %s\n",
- keyfile,strerror(errno));
- goto fail;
- }
-
- if(statbuf.st_size>65536)
- {
- fprintf(stderr,"Key %s too large for CERT encoding\n",keyfile);
- goto fail;
- }
-
- if(statbuf.st_size>16384)
- fprintf(stderr,"Warning: key file %s is larger than the default"
- " GnuPG max-cert-size\n",keyfile);
-
- printf("%s\tTYPE37\t\\# %u 0003 0000 00 ",
- name,(unsigned int)statbuf.st_size+5);
-
- err=1;
- while(err!=0)
- {
- unsigned char buffer[1024];
-
- err=read(fd,buffer,1024);
- if(err==-1)
- {
- fprintf(stderr,"Unable to read key file %s: %s\n",
- keyfile,strerror(errno));
- goto fail;
- }
-
- for(i=0;i<err;i++)
- printf("%02X",buffer[i]);
- }
-
- printf("\n");
-
- ret=0;
-
- fail:
- close(fd);
-
- return ret;
-}
-
-static int
-url_key(const char *name,const char *fpr,const char *url)
-{
- int len=6,fprlen=0;
-
- if(fpr)
- {
- const char *tmp = fpr;
- while (*tmp)
- {
- if ((*tmp >= 'A' && *tmp <= 'F') ||
- (*tmp >= 'a' && *tmp <= 'f') ||
- (*tmp >= '0' && *tmp <= '9'))
- {
- fprlen++;
- }
- else if (*tmp != ' ' && *tmp != '\t')
- {
- fprintf(stderr,"Fingerprint must consist of only hex digits"
- " and whitespace\n");
- return 1;
- }
-
- tmp++;
- }
-
- if(fprlen%2)
- {
- fprintf(stderr,"Fingerprint must be an even number of characters\n");
- return 1;
- }
-
- fprlen/=2;
- len+=fprlen;
- }
-
- if(url)
- len+=strlen(url);
-
- if(!fpr && !url)
- {
- fprintf(stderr,
- "Cannot generate a CERT without either a fingerprint or URL\n");
- return 1;
- }
-
- printf("%s\tTYPE37\t\\# %d 0006 0000 00 %02X",name,len,fprlen);
-
- if(fpr)
- printf(" %s",fpr);
-
- if(url)
- {
- const char *c;
- printf(" ");
- for(c=url;*c;c++)
- printf("%02X",*c);
- }
-
- printf("\n");
-
- return 0;
-}
-
-static void
-usage(FILE *stream)
-{
- fprintf(stream,"make-dns-cert\n");
- fprintf(stream,"\t-f\tfingerprint\n");
- fprintf(stream,"\t-u\tURL\n");
- fprintf(stream,"\t-k\tkey file\n");
- fprintf(stream,"\t-n\tDNS name\n");
-}
-
-int
-main(int argc,char *argv[])
-{
- int arg,err=1;
- char *fpr=NULL,*url=NULL,*keyfile=NULL,*name=NULL;
-
- if(argc==1)
- {
- usage(stderr);
- return 1;
- }
- else if(argc>1 && strcmp(argv[1],"--version")==0)
- {
- printf("make-dns-cert (GnuPG) " VERSION "\n");
- return 0;
- }
- else if(argc>1 && strcmp(argv[1],"--help")==0)
- {
- usage(stdout);
- return 0;
- }
-
- while((arg=getopt(argc,argv,"hf:u:k:n:"))!=-1)
- switch(arg)
- {
- default:
- case 'h':
- usage(stdout);
- exit(0);
-
- case 'f':
- fpr=optarg;
- break;
-
- case 'u':
- url=optarg;
- break;
-
- case 'k':
- keyfile=optarg;
- break;
-
- case 'n':
- name=optarg;
- break;
- }
-
- if(!name)
- {
- fprintf(stderr,"No name provided\n");
- return 1;
- }
-
- if(keyfile && (fpr || url))
- {
- fprintf(stderr,"Cannot generate a CERT record with both a keyfile and"
- " a fingerprint or URL\n");
- return 1;
- }
-
- if(keyfile)
- err=cert_key(name,keyfile);
- else
- err=url_key(name,fpr,url);
-
- return err;
-}
diff --git a/tools/mk-tdata.c b/tools/mk-tdata.c
deleted file mode 100644
index 833875d28..000000000
--- a/tools/mk-tdata.c
+++ /dev/null
@@ -1,67 +0,0 @@
-/* mk-tdata.c - Create some simple random testdata
- * Copyright (C) 1998, 1999, 2000, 2001, 2006 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 program 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.
- */
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-
-#ifndef RAND_MAX /* for SunOS */
-#define RAND_MAX 32767
-#endif
-
-int
-main(int argc, char **argv)
-{
- int i, c = 0;
- int limit =0;
- int char_mode = 0;
-
- if (argc)
- {
- argc--;
- argv++;
- }
-
- /* Check for option --char N */
- if (argc > 1 && !strcmp (argv[0], "--char"))
- {
- char_mode = 1;
- c = strtol (argv[1], NULL, 0);
- argc -= 2;
- argv += 2;
- }
-
- limit = argc ? atoi(argv[0]) : 0;
-
- srand(getpid());
-
- for (i=0; !limit || i < limit; i++ )
- {
- if (char_mode)
- {
- putchar (c);
- }
- else
- {
-#ifdef HAVE_RAND
- c = ((unsigned)(1 + (int) (256.0*rand()/(RAND_MAX+1.0)))-1);
-#else
- c = ((unsigned)(1 + (int) (256.0*random()/(RAND_MAX+1.0)))-1);
-#endif
- putchar (c);
- }
- }
- return 0;
-}
diff --git a/tools/mpicalc.c b/tools/mpicalc.c
deleted file mode 100644
index 915dcc32a..000000000
--- a/tools/mpicalc.c
+++ /dev/null
@@ -1,385 +0,0 @@
-/* mpitest.c - test the mpi functions
- * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
- *
- * This is an RPN calculator; values must be given in hex.
- * Operation is like dc(1) except that the input/output radix is
- * always 16 and you can use a '-' to prefix a negative number.
- * Addition operators: ++ and --. All operators must be delimited by a blank
- *
- *
- * 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 <ctype.h>
-
-#include "util.h"
-#include "mpi.h"
-#include "i18n.h"
-
-#define STACKSIZE 100
-static MPI stack[STACKSIZE];
-static int stackidx;
-
-
-const char *
-strusage( int level )
-{
- const char *p;
- switch( level ) {
- case 10:
- case 0: p = "mpicalc - v" VERSION "; "
- "Copyright 1997 Werner Koch (dd9jn)" ; break;
- case 13: p = "mpicalc"; break;
- case 14: p = VERSION; break;
- case 1:
- case 11: p = "Usage: mpicalc (-h for help)";
- break;
- case 2:
- case 12: p =
- "\nSyntax: mpicalc [options] [files]\n"
- "MPI RPN calculator\n";
- break;
- default: p = default_strusage(level);
- }
- return p;
-}
-
-
-static void
-i18n_init(void)
-{
-#ifdef ENABLE_NLS
- setlocale( LC_ALL, "" );
- bindtextdomain( PACKAGE, G10_LOCALEDIR );
- textdomain( PACKAGE );
-#endif
-}
-
-
-static void
-do_add(void)
-{
- if( stackidx < 2 ) {
- fputs("stack underflow\n",stderr);
- return;
- }
- mpi_add( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
- stackidx--;
-}
-
-static void
-do_sub(void)
-{
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_sub( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
- stackidx--;
-}
-
-static void
-do_inc(void)
-{
- if( stackidx < 1 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_add_ui( stack[stackidx-1], stack[stackidx-1], 1 );
-}
-
-static void
-do_dec(void)
-{
- if( stackidx < 1 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- /* mpi_sub_ui( stack[stackidx-1], stack[stackidx-1], 1 ); */
-}
-
-static void
-do_mul(void)
-{
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_mul( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
- stackidx--;
-}
-
-static void
-do_mulm(void)
-{
- if( stackidx < 3 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_mulm( stack[stackidx-3], stack[stackidx-3],
- stack[stackidx-2], stack[stackidx-1] );
- stackidx -= 2;
-}
-
-static void
-do_div(void)
-{
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_fdiv_q( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
- stackidx--;
-}
-
-static void
-do_rem(void)
-{
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_fdiv_r( stack[stackidx-2], stack[stackidx-2], stack[stackidx-1] );
- stackidx--;
-}
-
-static void
-do_powm(void)
-{
- MPI a;
- if( stackidx < 3 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- a= mpi_alloc(10);
- mpi_powm( a, stack[stackidx-3], stack[stackidx-2], stack[stackidx-1] );
- mpi_free(stack[stackidx-3]);
- stack[stackidx-3] = a;
- stackidx -= 2;
-}
-
-static void
-do_inv(void)
-{
- MPI a = mpi_alloc(40);
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_invm( a, stack[stackidx-2], stack[stackidx-1] );
- mpi_set(stack[stackidx-2],a);
- mpi_free(a);
- stackidx--;
-}
-
-static void
-do_gcd(void)
-{
- MPI a = mpi_alloc(40);
- if( stackidx < 2 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_gcd( a, stack[stackidx-2], stack[stackidx-1] );
- mpi_set(stack[stackidx-2],a);
- mpi_free(a);
- stackidx--;
-}
-
-static void
-do_rshift(void)
-{
- if( stackidx < 1 ) {
- fputs("stack underflow\n", stderr);
- return;
- }
- mpi_rshift( stack[stackidx-1],stack[stackidx-1], 1 );
-}
-
-
-int
-main(int argc, char **argv)
-{
- static ARGPARSE_OPTS opts[] = {
- {0} };
- ARGPARSE_ARGS pargs;
- int i, c;
- int state = 0;
- char strbuf[1000];
- int stridx=0;
-
- pargs.argc = &argc;
- pargs.argv = &argv;
- pargs.flags = 0;
-
- i18n_init();
- while( arg_parse( &pargs, opts) ) {
- switch( pargs.r_opt ) {
- default : pargs.err = 2; break;
- }
- }
- if( argc )
- usage(1);
-
-
- for(i=0; i < STACKSIZE; i++ )
- stack[i] = NULL;
- stackidx =0;
-
- while( (c=getc(stdin)) != EOF ) {
- if( !state ) { /* waiting */
- if( isdigit(c) ) {
- state = 1;
- ungetc(c, stdin);
- strbuf[0] = '0';
- strbuf[1] = 'x';
- stridx=2;
- }
- else if( isspace(c) )
- ;
- else {
- switch(c) {
- case '+':
- if( (c=getc(stdin)) == '+' )
- do_inc();
- else {
- ungetc(c, stdin);
- do_add();
- }
- break;
- case '-':
- if( (c=getc(stdin)) == '-' )
- do_dec();
- else if( isdigit(c) || (c >='A' && c <= 'F') ) {
- state = 1;
- ungetc(c, stdin);
- strbuf[0] = '-';
- strbuf[1] = '0';
- strbuf[2] = 'x';
- stridx=3;
- }
- else {
- ungetc(c, stdin);
- do_sub();
- }
- break;
- case '*':
- do_mul();
- break;
- case 'm':
- do_mulm();
- break;
- case '/':
- do_div();
- break;
- case '%':
- do_rem();
- break;
- case '^':
- do_powm();
- break;
- case 'I':
- do_inv();
- break;
- case 'G':
- do_gcd();
- break;
- case '>':
- do_rshift();
- break;
- case 'i': /* dummy */
- if( !stackidx )
- fputs("stack underflow\n", stderr);
- else {
- mpi_free(stack[stackidx-1]);
- stackidx--;
- }
- break;
- case 'd': /* duplicate the tos */
- if( !stackidx )
- fputs("stack underflow\n", stderr);
- else if( stackidx < STACKSIZE ) {
- mpi_free(stack[stackidx]);
- stack[stackidx] = mpi_copy( stack[stackidx-1] );
- stackidx++;
- }
- else
- fputs("stack overflow\n", stderr);
- break;
- case 'c':
- for(i=0; i < stackidx; i++ )
- mpi_free(stack[i]), stack[i] = NULL;
- stackidx = 0;
- break;
- case 'p': /* print the tos */
- if( !stackidx )
- puts("stack is empty");
- else {
- mpi_print(stdout, stack[stackidx-1], 1 );
- putchar('\n');
- }
- break;
- case 'f': /* print the stack */
- for( i = stackidx-1 ; i >= 0; i-- ) {
- printf("[%2d]: ", i );
- mpi_print(stdout, stack[i], 1 );
- putchar('\n');
- }
- break;
- default:
- fputs("invalid operator\n", stderr);
- }
- }
- }
- else if( state == 1 ) { /* in a number */
- if( !isxdigit(c) ) { /* store the number */
- state = 0;
- ungetc(c, stdin);
- if( stridx < 1000 )
- strbuf[stridx] = 0;
-
- if( stackidx < STACKSIZE ) {
- if( !stack[stackidx] )
- stack[stackidx] = mpi_alloc(10);
- if( mpi_fromstr(stack[stackidx], strbuf) )
- fputs("invalid number\n", stderr);
- else
- stackidx++;
- }
- else
- fputs("stack overflow\n", stderr);
- }
- else { /* store digit */
- if( stridx < 999 )
- strbuf[stridx++] = c;
- else if( stridx == 999 ) {
- strbuf[stridx] = 0;
- fputs("string too large - truncated\n", stderr);
- stridx++;
- }
- }
- }
-
- }
- for(i=0; i < stackidx; i++ )
- mpi_free(stack[i]);
- return 0;
-}
diff --git a/tools/no-libgcrypt.c b/tools/no-libgcrypt.c
new file mode 100644
index 000000000..636df10c6
--- /dev/null
+++ b/tools/no-libgcrypt.c
@@ -0,0 +1,112 @@
+/* no-libgcrypt.c - Replacement functions for libgcrypt.
+ * 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.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "../common/util.h"
+#include "i18n.h"
+
+
+/* Replace libgcrypt's malloc functions which are used by
+ ../jnlib/libjnlib.a . ../common/util.h defines macros to map them
+ to xmalloc etc. */
+static void
+out_of_core (void)
+{
+ log_fatal (_("error allocating enough memory: %s\n"), strerror (errno));
+}
+
+
+void *
+gcry_malloc (size_t n)
+{
+ return malloc (n);
+}
+
+void *
+gcry_xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p)
+ out_of_core ();
+ return p;
+}
+
+char *
+gcry_strdup (const char *string)
+{
+ return malloc (strlen (string)+1);
+}
+
+
+void *
+gcry_realloc (void *a, size_t n)
+{
+ return realloc (a, n);
+}
+
+void *
+gcry_xrealloc (void *a, size_t n)
+{
+ void *p = realloc (a, n);
+ if (!p)
+ out_of_core ();
+ return p;
+}
+
+
+
+void *
+gcry_calloc (size_t n, size_t m)
+{
+ return calloc (n, m);
+}
+
+void *
+gcry_xcalloc (size_t n, size_t m)
+{
+ void *p = calloc (n, m);
+ if (!p)
+ out_of_core ();
+ return p;
+}
+
+
+char *
+gcry_xstrdup (const char *string)
+{
+ void *p = malloc (strlen (string)+1);
+ if (!p)
+ out_of_core ();
+ strcpy( p, string );
+ return p;
+}
+
+void
+gcry_free (void *a)
+{
+ if (a)
+ free (a);
+}
diff --git a/tools/pgpgroup-to-gpggroup b/tools/pgpgroup-to-gpggroup
deleted file mode 100755
index 5ea9990b6..000000000
--- a/tools/pgpgroup-to-gpggroup
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/perl -w
-
-# pgpgroup-to-gpggroup - convert PGP groups to GnuPG groups
-# Copyright (C) 2004 Free Software Foundation, Inc.
-#
-# This program 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.
-#
-# This program 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-# This program requires a PGP command line program that supports
-# groups (note that the PGP 8 command line doesn't).
-
-$pgp="pgp -gvv 2>/dev/null";
-
-open(PGP,"$pgp|") || die "Unable to call PGP: $!";
-
-while(<PGP>)
-{
- # If the line begins with a ">", then it is a new group.
-
- if(/^ > (\S+)/)
- {
- print "\ngroup $1 = ";
- }
- elsif(/\s+(0x\S+)/)
- {
- print "$1 ";
- }
-}
-
-print "\n";
-close(PGP);
diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c
new file mode 100644
index 000000000..303ddad13
--- /dev/null
+++ b/tools/rfc822parse.c
@@ -0,0 +1,1258 @@
+/* rfc822parse.c - Simple mail and MIME parser
+ * Copyright (C) 1999, 2000 Werner Koch, Duesseldorf
+ * Copyright (C) 2003, 2004 g10 Code GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+
+/* According to RFC822 binary zeroes are allowed at many places. We do
+ * not handle this correct especially in the field parsing code. It
+ * should be easy to fix and the API provides a interfaces which
+ * returns the length but in addition makes sure that returned strings
+ * are always ended by a \0.
+ *
+ * Furthermore, the case of field names is changed and thus it is not
+ * always a good idea to use these modified header
+ * lines (e.g. signatures may break).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include "rfc822parse.h"
+
+enum token_type
+ {
+ tSPACE,
+ tATOM,
+ tQUOTED,
+ tDOMAINLIT,
+ tSPECIAL
+ };
+
+/* For now we directly use our TOKEN as the parse context */
+typedef struct rfc822parse_field_context *TOKEN;
+struct rfc822parse_field_context
+{
+ TOKEN next;
+ enum token_type type;
+ struct {
+ unsigned int cont:1;
+ unsigned int lowered:1;
+ } flags;
+ /*TOKEN owner_pantry; */
+ char data[1];
+};
+
+struct hdr_line
+{
+ struct hdr_line *next;
+ int cont; /* This is a continuation of the previous line. */
+ unsigned char line[1];
+};
+
+typedef struct hdr_line *HDR_LINE;
+
+
+struct part
+{
+ struct part *right; /* The next part. */
+ struct part *down; /* A contained part. */
+ HDR_LINE hdr_lines; /* Header lines os that part. */
+ HDR_LINE *hdr_lines_tail; /* Helper for adding lines. */
+ char *boundary; /* Only used in the first part. */
+};
+typedef struct part *part_t;
+
+struct rfc822parse_context
+{
+ rfc822parse_cb_t callback;
+ void *callback_value;
+ int callback_error;
+ int in_body;
+ int in_preamble; /* Wether we are before the first boundary. */
+ part_t parts; /* The tree of parts. */
+ part_t current_part; /* Whom we are processing (points into parts). */
+ const char *boundary; /* Current boundary. */
+};
+
+static HDR_LINE find_header (rfc822parse_t msg, const char *name,
+ int which, HDR_LINE * rprev);
+
+
+static size_t
+length_sans_trailing_ws (const unsigned char *line, size_t len)
+{
+ const unsigned char *p, *mark;
+ size_t n;
+
+ for (mark=NULL, p=line, n=0; n < len; n++, p++)
+ {
+ if (strchr (" \t\r\n", *p ))
+ {
+ if( !mark )
+ mark = p;
+ }
+ else
+ mark = NULL;
+ }
+
+ if (mark)
+ return mark - line;
+ return len;
+}
+
+
+static void
+lowercase_string (unsigned char *string)
+{
+ for (; *string; string++)
+ if (*string >= 'A' && *string <= 'Z')
+ *string = *string - 'A' + 'a';
+}
+
+/* Transform a header name into a standard capitalized format; i.e
+ "Content-Type". Conversion stops at the colon. As usual we don't
+ use the localized versions of ctype.h.
+ */
+static void
+capitalize_header_name (unsigned char *name)
+{
+ int first = 1;
+
+ for (; *name && *name != ':'; name++)
+ if (*name == '-')
+ first = 1;
+ else if (first)
+ {
+ if (*name >= 'a' && *name <= 'z')
+ *name = *name - 'a' + 'A';
+ first = 0;
+ }
+ else if (*name >= 'A' && *name <= 'Z')
+ *name = *name - 'A' + 'a';
+}
+
+#ifndef HAVE_STPCPY
+static char *
+stpcpy (char *a,const char *b)
+{
+ while (*b)
+ *a++ = *b++;
+ *a = 0;
+
+ return (char*)a;
+}
+#endif
+
+
+/* If a callback has been registerd, call it for the event of type
+ EVENT. */
+static int
+do_callback (rfc822parse_t msg, rfc822parse_event_t event)
+{
+ int rc;
+
+ if (!msg->callback || msg->callback_error)
+ return 0;
+ rc = msg->callback (msg->callback_value, event, msg);
+ if (rc)
+ msg->callback_error = rc;
+ return rc;
+}
+
+static part_t
+new_part (void)
+{
+ part_t part;
+
+ part = calloc (1, sizeof *part);
+ if (part)
+ {
+ part->hdr_lines_tail = &part->hdr_lines;
+ }
+ return part;
+}
+
+
+static void
+release_part (part_t part)
+{
+ part_t tmp;
+ HDR_LINE hdr, hdr2;
+
+ for (; part; part = tmp)
+ {
+ tmp = part->right;
+ if (part->down)
+ release_part (part->down);
+ for (hdr = part->hdr_lines; hdr; hdr = hdr2)
+ {
+ hdr2 = hdr->next;
+ free (hdr);
+ }
+ free (part->boundary);
+ free (part);
+ }
+}
+
+
+static void
+release_handle_data (rfc822parse_t msg)
+{
+ release_part (msg->parts);
+ msg->parts = NULL;
+ msg->current_part = NULL;
+ msg->boundary = NULL;
+}
+
+
+/* Create a new parsing context for an entire rfc822 message and
+ return it. CB and CB_VALUE may be given to callback for certain
+ events. NULL is returned on error with errno set appropriately. */
+rfc822parse_t
+rfc822parse_open (rfc822parse_cb_t cb, void *cb_value)
+{
+ rfc822parse_t msg = calloc (1, sizeof *msg);
+ if (msg)
+ {
+ msg->parts = msg->current_part = new_part ();
+ if (!msg->parts)
+ {
+ free (msg);
+ msg = NULL;
+ }
+ else
+ {
+ msg->callback = cb;
+ msg->callback_value = cb_value;
+ if (do_callback (msg, RFC822PARSE_OPEN))
+ {
+ release_handle_data (msg);
+ free (msg);
+ msg = NULL;
+ }
+ }
+ }
+ return msg;
+}
+
+
+void
+rfc822parse_cancel (rfc822parse_t msg)
+{
+ if (msg)
+ {
+ do_callback (msg, RFC822PARSE_CANCEL);
+ release_handle_data (msg);
+ free (msg);
+ }
+}
+
+
+void
+rfc822parse_close (rfc822parse_t msg)
+{
+ if (msg)
+ {
+ do_callback (msg, RFC822PARSE_CLOSE);
+ release_handle_data (msg);
+ free (msg);
+ }
+}
+
+static part_t
+find_parent (part_t tree, part_t target)
+{
+ part_t part;
+
+ for (part = tree->down; part; part = part->right)
+ {
+ if (part == target)
+ return tree; /* Found. */
+ if (part->down)
+ {
+ part_t tmp = find_parent (part, target);
+ if (tmp)
+ return tmp;
+ }
+ }
+ return NULL;
+}
+
+static void
+set_current_part_to_parent (rfc822parse_t msg)
+{
+ part_t parent;
+
+ assert (msg->current_part);
+ parent = find_parent (msg->parts, msg->current_part);
+ if (!parent)
+ return; /* Already at the top. */
+
+#ifndef NDEBUG
+ {
+ part_t part;
+ for (part = parent->down; part; part = part->right)
+ if (part == msg->current_part)
+ break;
+ assert (part);
+ }
+#endif
+ msg->current_part = parent;
+
+ parent = find_parent (msg->parts, parent);
+ msg->boundary = parent? parent->boundary: NULL;
+}
+
+
+
+/****************
+ * We have read in all header lines and are about to receive the body
+ * part. The delimiter line has already been processed.
+ *
+ * FIXME: we's better return an error in case of memory failures.
+ */
+static int
+transition_to_body (rfc822parse_t msg)
+{
+ rfc822parse_field_t ctx;
+ int rc;
+
+ rc = do_callback (msg, RFC822PARSE_T2BODY);
+ if (!rc)
+ {
+ /* Store the boundary if we have multipart type. */
+ ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+ if (ctx)
+ {
+ const char *s;
+
+ s = rfc822parse_query_media_type (ctx, NULL);
+ if (s && !strcmp (s,"multipart"))
+ {
+ s = rfc822parse_query_parameter (ctx, "boundary", 0);
+ if (s)
+ {
+ assert (!msg->current_part->boundary);
+ msg->current_part->boundary = malloc (strlen (s) + 1);
+ if (msg->current_part->boundary)
+ {
+ part_t part;
+
+ strcpy (msg->current_part->boundary, s);
+ msg->boundary = msg->current_part->boundary;
+ part = new_part ();
+ if (!part)
+ {
+ int save_errno = errno;
+ rfc822parse_release_field (ctx);
+ errno = save_errno;
+ return -1;
+ }
+ rc = do_callback (msg, RFC822PARSE_LEVEL_DOWN);
+ assert (!msg->current_part->down);
+ msg->current_part->down = part;
+ msg->current_part = part;
+ msg->in_preamble = 1;
+ }
+ }
+ }
+ rfc822parse_release_field (ctx);
+ }
+ }
+
+ return rc;
+}
+
+/* We have just passed a MIME boundary and need to prepare for new part.
+ headers. */
+static int
+transition_to_header (rfc822parse_t msg)
+{
+ part_t part;
+
+ assert (msg->current_part);
+ assert (!msg->current_part->right);
+
+ part = new_part ();
+ if (!part)
+ return -1;
+
+ msg->current_part->right = part;
+ msg->current_part = part;
+ return 0;
+}
+
+
+static int
+insert_header (rfc822parse_t msg, const unsigned char *line, size_t length)
+{
+ HDR_LINE hdr;
+
+ assert (msg->current_part);
+ if (!length)
+ {
+ msg->in_body = 1;
+ return transition_to_body (msg);
+ }
+
+ if (!msg->current_part->hdr_lines)
+ do_callback (msg, RFC822PARSE_BEGIN_HEADER);
+
+ length = length_sans_trailing_ws (line, length);
+ hdr = malloc (sizeof (*hdr) + length);
+ if (!hdr)
+ return -1;
+ hdr->next = NULL;
+ hdr->cont = (*line == ' ' || *line == '\t');
+ memcpy (hdr->line, line, length);
+ hdr->line[length] = 0; /* Make it a string. */
+
+ /* Transform a field name into canonical format. */
+ if (!hdr->cont && strchr (line, ':'))
+ capitalize_header_name (hdr->line);
+
+ *msg->current_part->hdr_lines_tail = hdr;
+ msg->current_part->hdr_lines_tail = &hdr->next;
+
+ /* Lets help the caller to prevent mail loops and issue an event for
+ * every Received header. */
+ if (length >= 9 && !memcmp (line, "Received:", 9))
+ do_callback (msg, RFC822PARSE_RCVD_SEEN);
+ return 0;
+}
+
+
+/****************
+ * Note: We handle the body transparent to allow binary zeroes in it.
+ */
+static int
+insert_body (rfc822parse_t msg, const unsigned char *line, size_t length)
+{
+ int rc = 0;
+
+ if (length > 2 && *line == '-' && line[1] == '-' && msg->boundary)
+ {
+ size_t blen = strlen (msg->boundary);
+
+ if (length == blen + 2
+ && !memcmp (line+2, msg->boundary, blen))
+ {
+ rc = do_callback (msg, RFC822PARSE_BOUNDARY);
+ msg->in_body = 0;
+ if (!rc && !msg->in_preamble)
+ rc = transition_to_header (msg);
+ msg->in_preamble = 0;
+ }
+ else if (length == blen + 4
+ && line[length-2] =='-' && line[length-1] == '-'
+ && !memcmp (line+2, msg->boundary, blen))
+ {
+ rc = do_callback (msg, RFC822PARSE_LAST_BOUNDARY);
+ msg->boundary = NULL; /* No current boundary anymore. */
+ set_current_part_to_parent (msg);
+
+ /* Fixme: The next should actually be send right before the
+ next boundary, so that we can mark the epilogue. */
+ if (!rc)
+ rc = do_callback (msg, RFC822PARSE_LEVEL_UP);
+ }
+ }
+ if (msg->in_preamble && !rc)
+ rc = do_callback (msg, RFC822PARSE_PREAMBLE);
+
+ return rc;
+}
+
+/* Insert the next line into the parser. Return 0 on success or true
+ on error with errno set appropriately. */
+int
+rfc822parse_insert (rfc822parse_t msg, const unsigned char *line, size_t length)
+{
+ return (msg->in_body
+ ? insert_body (msg, line, length)
+ : insert_header (msg, line, length));
+}
+
+
+/* Tell the parser that we have finished the message. */
+int
+rfc822parse_finish (rfc822parse_t msg)
+{
+ return do_callback (msg, RFC822PARSE_FINISH);
+}
+
+
+
+/****************
+ * Get a copy of a header line. The line is returned as one long
+ * string with LF to separate the continuation line. Caller must free
+ * the return buffer. WHICH may be used to enumerate over all lines.
+ * Wildcards are allowed. This function works on the current headers;
+ * i.e. the regular mail headers or the MIME headers of the current
+ * part.
+ *
+ * WHICH gives the mode:
+ * -1 := Take the last occurence
+ * n := Take the n-th one.
+ *
+ * Returns a newly allocated buffer or NULL on error. errno is set in
+ * case of a memory failure or set to 0 if the requested field is not
+ * available.
+ *
+ * If VALUEOFF is not NULL it will receive the offset of the first non
+ * space character in the value part of the line (i.e. after the first
+ * colon).
+ */
+char *
+rfc822parse_get_field (rfc822parse_t msg, const char *name, int which,
+ size_t *valueoff)
+{
+ HDR_LINE h, h2;
+ char *buf, *p;
+ size_t n;
+
+ h = find_header (msg, name, which, NULL);
+ if (!h)
+ {
+ errno = 0;
+ return NULL; /* no such field */
+ }
+
+ n = strlen (h->line) + 1;
+ for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
+ n += strlen (h2->line) + 1;
+
+ buf = p = malloc (n);
+ if (buf)
+ {
+ p = stpcpy (p, h->line);
+ *p++ = '\n';
+ for (h2 = h->next; h2 && h2->cont; h2 = h2->next)
+ {
+ p = stpcpy (p, h2->line);
+ *p++ = '\n';
+ }
+ p[-1] = 0;
+ }
+
+ if (valueoff)
+ {
+ p = strchr (buf, ':');
+ if (!p)
+ *valueoff = 0; /* Oops: should never happen. */
+ else
+ {
+ p++;
+ while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n')
+ p++;
+ *valueoff = p - buf;
+ }
+ }
+
+ return buf;
+}
+
+
+/****************
+ * Enumerate all header. Caller has to provide the address of a pointer
+ * which has to be initialzed to NULL, the caller should then never change this
+ * pointer until he has closed the enumeration by passing again the address
+ * of the pointer but with msg set to NULL.
+ * The function returns pointers to all the header lines or NULL when
+ * all lines have been enumerated or no headers are available.
+ */
+const char *
+rfc822parse_enum_header_lines (rfc822parse_t msg, void **context)
+{
+ HDR_LINE l;
+
+ if (!msg) /* Close. */
+ return NULL;
+
+ if (*context == msg || !msg->current_part)
+ return NULL;
+
+ l = *context ? (HDR_LINE) *context : msg->current_part->hdr_lines;
+
+ if (l)
+ {
+ *context = l->next ? (void *) (l->next) : (void *) msg;
+ return l->line;
+ }
+ *context = msg; /* Mark end of list. */
+ return NULL;
+}
+
+
+
+/****************
+ * Find a header field. If the Name does end in an asterisk this is meant
+ * to be a wildcard.
+ *
+ * which -1 : Retrieve the last field
+ * >0 : Retrieve the n-th field
+
+ * RPREV may be used to return the predecessor of the returned field;
+ * which may be NULL for the very first one. It has to be initialzed
+ * to either NULL in which case the search start at the first header line,
+ * or it may point to a headerline, where the search should start
+ */
+static HDR_LINE
+find_header (rfc822parse_t msg, const char *name, int which, HDR_LINE *rprev)
+{
+ HDR_LINE hdr, prev = NULL, mark = NULL;
+ unsigned char *p;
+ size_t namelen, n;
+ int found = 0;
+ int glob = 0;
+
+ if (!msg->current_part)
+ return NULL;
+
+ namelen = strlen (name);
+ if (namelen && name[namelen - 1] == '*')
+ {
+ namelen--;
+ glob = 1;
+ }
+
+ hdr = msg->current_part->hdr_lines;
+ if (rprev && *rprev)
+ {
+ /* spool forward to the requested starting place.
+ * we cannot simply set this as we have to return
+ * the previous list element too */
+ for (; hdr && hdr != *rprev; prev = hdr, hdr = hdr->next)
+ ;
+ }
+
+ for (; hdr; prev = hdr, hdr = hdr->next)
+ {
+ if (hdr->cont)
+ continue;
+ if (!(p = strchr (hdr->line, ':')))
+ continue; /* invalid header, just skip it. */
+ n = p - hdr->line;
+ if (!n)
+ continue; /* invalid name */
+ if ((glob ? (namelen <= n) : (namelen == n))
+ && !memcmp (hdr->line, name, namelen))
+ {
+ found++;
+ if (which == -1)
+ mark = hdr;
+ else if (found == which)
+ {
+ if (rprev)
+ *rprev = prev;
+ return hdr;
+ }
+ }
+ }
+ if (mark && rprev)
+ *rprev = prev;
+ return mark;
+}
+
+
+
+static const char *
+skip_ws (const char *s)
+{
+ while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
+ s++;
+ return s;
+}
+
+
+static void
+release_token_list (TOKEN t)
+{
+ while (t)
+ {
+ TOKEN t2 = t->next;
+ /* fixme: If we have owner_pantry, put the token back to
+ * this pantry so that it can be reused later */
+ free (t);
+ t = t2;
+ }
+}
+
+
+static TOKEN
+new_token (enum token_type type, const char *buf, size_t length)
+{
+ TOKEN t;
+
+ /* fixme: look through our pantries to find a suitable
+ * token for reuse */
+ t = malloc (sizeof *t + length);
+ if (t)
+ {
+ t->next = NULL;
+ t->type = type;
+ memset (&t->flags, 0, sizeof (t->flags));
+ t->data[0] = 0;
+ if (buf)
+ {
+ memcpy (t->data, buf, length);
+ t->data[length] = 0; /* Make sure it is a C string. */
+ }
+ else
+ t->data[0] = 0;
+ }
+ return t;
+}
+
+static TOKEN
+append_to_token (TOKEN old, const char *buf, size_t length)
+{
+ size_t n = strlen (old->data);
+ TOKEN t;
+
+ t = malloc (sizeof *t + n + length);
+ if (t)
+ {
+ t->next = old->next;
+ t->type = old->type;
+ t->flags = old->flags;
+ memcpy (t->data, old->data, n);
+ memcpy (t->data + n, buf, length);
+ t->data[n + length] = 0;
+ old->next = NULL;
+ release_token_list (old);
+ }
+ return t;
+}
+
+
+
+/*
+ Parse a field into tokens as defined by rfc822.
+ */
+static TOKEN
+parse_field (HDR_LINE hdr)
+{
+ static const char specials[] = "<>@.,;:\\[]\"()";
+ static const char specials2[] = "<>@.,;:";
+ static const char tspecials[] = "/?=<>@,;:\\[]\"()";
+ static const char tspecials2[] = "/?=<>@.,;:"; /* FIXME: really
+ include '.'?*/
+ static struct
+ {
+ const unsigned char *name;
+ size_t namelen;
+ } tspecial_header[] = {
+ { "Content-Type", 12},
+ { "Content-Transfer-Encoding", 25},
+ { "Content-Disposition", 19},
+ { NULL, 0}
+ };
+ const char *delimiters;
+ const char *delimiters2;
+ const unsigned char *line, *s, *s2;
+ size_t n;
+ int i, invalid = 0;
+ TOKEN t, tok, *tok_tail;
+
+ errno = 0;
+ if (!hdr)
+ return NULL;
+
+ tok = NULL;
+ tok_tail = &tok;
+
+ line = hdr->line;
+ if (!(s = strchr (line, ':')))
+ return NULL; /* oops */
+
+ n = s - line;
+ if (!n)
+ return NULL; /* oops: invalid name */
+
+ delimiters = specials;
+ delimiters2 = specials2;
+ for (i = 0; tspecial_header[i].name; i++)
+ {
+ if (n == tspecial_header[i].namelen
+ && !memcmp (line, tspecial_header[i].name, n))
+ {
+ delimiters = tspecials;
+ delimiters2 = tspecials2;
+ break;
+ }
+ }
+
+ s++; /* Move over the colon. */
+ for (;;)
+ {
+ if (!*s)
+ {
+ if (!hdr->next || !hdr->next->cont)
+ break;
+ hdr = hdr->next;
+ s = hdr->line;
+ }
+
+ if (*s == '(')
+ {
+ int level = 1;
+ int in_quote = 0;
+
+ invalid = 0;
+ for (s++;; s++)
+ {
+ if (!*s)
+ {
+ if (!hdr->next || !hdr->next->cont)
+ break;
+ hdr = hdr->next;
+ s = hdr->line;
+ }
+
+ if (in_quote)
+ {
+ if (*s == '\"')
+ in_quote = 0;
+ else if (*s == '\\' && s[1]) /* what about continuation? */
+ s++;
+ }
+ else if (*s == ')')
+ {
+ if (!--level)
+ break;
+ }
+ else if (*s == '(')
+ level++;
+ else if (*s == '\"')
+ in_quote = 1;
+ }
+ if (!*s)
+ ; /* Actually this is an error, but we don't care about it. */
+ else
+ s++;
+ }
+ else if (*s == '\"' || *s == '[')
+ {
+ /* We do not check for non-allowed nesting of domainliterals */
+ int term = *s == '\"' ? '\"' : ']';
+ invalid = 0;
+ s++;
+ t = NULL;
+
+ for (;;)
+ {
+ for (s2 = s; *s2; s2++)
+ {
+ if (*s2 == term)
+ break;
+ else if (*s2 == '\\' && s2[1]) /* what about continuation? */
+ s2++;
+ }
+
+ t = (t
+ ? append_to_token (t, s, s2 - s)
+ : new_token (term == '\"'? tQUOTED : tDOMAINLIT, s, s2 - s));
+ if (!t)
+ goto failure;
+
+ if (*s2 || !hdr->next || !hdr->next->cont)
+ break;
+ hdr = hdr->next;
+ s = hdr->line;
+ }
+ *tok_tail = t;
+ tok_tail = &t->next;
+ s = s2;
+ if (*s)
+ s++; /* skip the delimiter */
+ }
+ else if ((s2 = strchr (delimiters2, *s)))
+ { /* Special characters which are not handled above. */
+ invalid = 0;
+ t = new_token (tSPECIAL, s, 1);
+ if (!t)
+ goto failure;
+ *tok_tail = t;
+ tok_tail = &t->next;
+ s++;
+ }
+ else if (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n')
+ {
+ invalid = 0;
+ s = skip_ws (s + 1);
+ }
+ else if (*s > 0x20 && !(*s & 128))
+ { /* Atom. */
+ invalid = 0;
+ for (s2 = s + 1; *s2 > 0x20
+ && !(*s2 & 128) && !strchr (delimiters, *s2); s2++)
+ ;
+ t = new_token (tATOM, s, s2 - s);
+ if (!t)
+ goto failure;
+ *tok_tail = t;
+ tok_tail = &t->next;
+ s = s2;
+ }
+ else
+ { /* Invalid character. */
+ if (!invalid)
+ { /* For parsing we assume only one space. */
+ t = new_token (tSPACE, NULL, 0);
+ if (!t)
+ goto failure;
+ *tok_tail = t;
+ tok_tail = &t->next;
+ invalid = 1;
+ }
+ s++;
+ }
+ }
+
+ return tok;
+
+ failure:
+ {
+ int save = errno;
+ release_token_list (tok);
+ errno = save;
+ }
+ return NULL;
+}
+
+
+
+
+/****************
+ * Find and parse a header field.
+ * WHICH indicates what to do if there are multiple instance of the same
+ * field (like "Received"); the following value are defined:
+ * -1 := Take the last occurence
+ * 0 := Reserved
+ * n := Take the n-th one.
+ * Returns a handle for further operations on the parse context of the field
+ * or NULL if the field was not found.
+ */
+rfc822parse_field_t
+rfc822parse_parse_field (rfc822parse_t msg, const char *name, int which)
+{
+ HDR_LINE hdr;
+
+ if (!which)
+ return NULL;
+
+ hdr = find_header (msg, name, which, NULL);
+ if (!hdr)
+ return NULL;
+ return parse_field (hdr);
+}
+
+void
+rfc822parse_release_field (rfc822parse_field_t ctx)
+{
+ if (ctx)
+ release_token_list (ctx);
+}
+
+
+
+/****************
+ * Check whether T points to a parameter.
+ * A parameter starts with a semicolon and it is assumed that t
+ * points to exactly this one.
+ */
+static int
+is_parameter (TOKEN t)
+{
+ t = t->next;
+ if (!t || t->type != tATOM)
+ return 0;
+ t = t->next;
+ if (!t || !(t->type == tSPECIAL && t->data[0] == '='))
+ return 0;
+ t = t->next;
+ if (!t)
+ return 1; /* We assume that an non existing value is an empty one. */
+ return t->type == tQUOTED || t->type == tATOM;
+}
+
+/*
+ Some header (Content-type) have a special syntax where attribute=value
+ pairs are used after a leading semicolon. The parse_field code
+ knows about these fields and changes the parsing to the one defined
+ in RFC2045.
+ Returns a pointer to the value which is valid as long as the
+ parse context is valid; NULL is returned in case that attr is not
+ defined in the header, a missing value is reppresented by an empty string.
+
+ With LOWER_VALUE set to true, a matching field valuebe be
+ lowercased.
+
+ Note, that ATTR should be lowercase.
+ */
+const char *
+rfc822parse_query_parameter (rfc822parse_field_t ctx, const char *attr,
+ int lower_value)
+{
+ TOKEN t, a;
+
+ for (t = ctx; t; t = t->next)
+ {
+ /* skip to the next semicolon */
+ for (; t && !(t->type == tSPECIAL && t->data[0] == ';'); t = t->next)
+ ;
+ if (!t)
+ return NULL;
+ if (is_parameter (t))
+ { /* Look closer. */
+ a = t->next; /* We know that this is an atom */
+ if ( !a->flags.lowered )
+ {
+ lowercase_string (a->data);
+ a->flags.lowered = 1;
+ }
+ if (!strcmp (a->data, attr))
+ { /* found */
+ t = a->next->next;
+ /* Either T is now an atom, a quoted string or NULL in
+ * which case we return an empty string. */
+
+ if ( lower_value && t && !t->flags.lowered )
+ {
+ lowercase_string (t->data);
+ t->flags.lowered = 1;
+ }
+ return t ? t->data : "";
+ }
+ }
+ }
+ return NULL;
+}
+
+/****************
+ * This function may be used for the Content-Type header to figure out
+ * the media type and subtype. Note, that the returned strings are
+ * guaranteed to be lowercase as required by MIME.
+ *
+ * Returns: a pointer to the media type and if subtype is not NULL,
+ * a pointer to the subtype.
+ */
+const char *
+rfc822parse_query_media_type (rfc822parse_field_t ctx, const char **subtype)
+{
+ TOKEN t = ctx;
+ const char *type;
+
+ if (t->type != tATOM)
+ return NULL;
+ if (!t->flags.lowered)
+ {
+ lowercase_string (t->data);
+ t->flags.lowered = 1;
+ }
+ type = t->data;
+ t = t->next;
+ if (!t || t->type != tSPECIAL || t->data[0] != '/')
+ return NULL;
+ t = t->next;
+ if (!t || t->type != tATOM)
+ return NULL;
+
+ if (subtype)
+ {
+ if (!t->flags.lowered)
+ {
+ lowercase_string (t->data);
+ t->flags.lowered = 1;
+ }
+ *subtype = t->data;
+ }
+ return type;
+}
+
+
+
+
+
+#ifdef TESTING
+
+/* Internal debug function to print the structure of the message. */
+static void
+dump_structure (rfc822parse_t msg, part_t part, int indent)
+{
+ if (!part)
+ {
+ printf ("*** Structure of this message:\n");
+ part = msg->parts;
+ }
+
+ for (; part; part = part->right)
+ {
+ rfc822parse_field_t ctx;
+ part_t save_part; /* ugly hack - we should have a function to
+ get part inforation. */
+ const char *s;
+
+ save_part = msg->current_part;
+ msg->current_part = part;
+ ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+ msg->current_part = save_part;
+ if (ctx)
+ {
+ const char *s1, *s2;
+ s1 = rfc822parse_query_media_type (ctx, &s2);
+ if (s1)
+ printf ("*** %*s %s/%s", indent*2, "", s1, s2);
+ else
+ printf ("*** %*s [not found]", indent*2, "");
+
+ s = rfc822parse_query_parameter (ctx, "boundary", 0);
+ if (s)
+ printf (" (boundary=\"%s\")", s);
+ rfc822parse_release_field (ctx);
+ }
+ else
+ printf ("*** %*s text/plain [assumed]", indent*2, "");
+ putchar('\n');
+
+ if (part->down)
+ dump_structure (msg, part->down, indent + 1);
+ }
+
+}
+
+
+
+static void
+show_param (rfc822parse_field_t ctx, const char *name)
+{
+ const char *s;
+
+ if (!ctx)
+ return;
+ s = rfc822parse_query_parameter (ctx, name, 0);
+ if (s)
+ printf ("*** %s: `%s'\n", name, s);
+}
+
+
+
+static void
+show_event (rfc822parse_event_t event)
+{
+ const char *s;
+
+ switch (event)
+ {
+ case RFC822PARSE_OPEN: s= "Open"; break;
+ case RFC822PARSE_CLOSE: s= "Close"; break;
+ case RFC822PARSE_CANCEL: s= "Cancel"; break;
+ case RFC822PARSE_T2BODY: s= "T2Body"; break;
+ case RFC822PARSE_FINISH: s= "Finish"; break;
+ case RFC822PARSE_RCVD_SEEN: s= "Rcvd_Seen"; break;
+ case RFC822PARSE_BOUNDARY: s= "Boundary"; break;
+ case RFC822PARSE_LAST_BOUNDARY: s= "Last_Boundary"; break;
+ default: s= "***invalid event***"; break;
+ }
+ printf ("*** got RFC822 event %s\n", s);
+}
+
+static int
+msg_cb (void *dummy_arg, rfc822parse_event_t event, rfc822parse_t msg)
+{
+ show_event (event);
+ if (event == RFC822PARSE_T2BODY)
+ {
+ rfc822parse_field_t ctx;
+ void *ectx;
+ const char *line;
+
+ for (ectx=NULL; (line = rfc822parse_enum_header_lines (msg, &ectx)); )
+ {
+ printf ("*** HDR: %s\n", line);
+ }
+ rfc822parse_enum_header_lines (NULL, &ectx); /* Close enumerator. */
+
+ ctx = rfc822parse_parse_field (msg, "Content-Type", -1);
+ if (ctx)
+ {
+ const char *s1, *s2;
+ s1 = rfc822parse_query_media_type (ctx, &s2);
+ if (s1)
+ printf ("*** media: `%s/%s'\n", s1, s2);
+ else
+ printf ("*** media: [not found]\n");
+ show_param (ctx, "boundary");
+ show_param (ctx, "protocol");
+ rfc822parse_release_field (ctx);
+ }
+ else
+ printf ("*** media: text/plain [assumed]\n");
+
+ }
+
+
+ return 0;
+}
+
+
+
+int
+main (int argc, char **argv)
+{
+ char line[5000];
+ size_t length;
+ rfc822parse_t msg;
+
+ msg = rfc822parse_open (msg_cb, NULL);
+ if (!msg)
+ abort ();
+
+ while (fgets (line, sizeof (line), stdin))
+ {
+ length = strlen (line);
+ if (length && line[length - 1] == '\n')
+ line[--length] = 0;
+ if (length && line[length - 1] == '\r')
+ line[--length] = 0;
+ if (rfc822parse_insert (msg, line, length))
+ abort ();
+ }
+
+ dump_structure (msg, NULL, 0);
+
+ rfc822parse_close (msg);
+ return 0;
+}
+#endif
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -DTESTING -o rfc822parse rfc822parse.c"
+End:
+*/
diff --git a/tools/rfc822parse.h b/tools/rfc822parse.h
new file mode 100644
index 000000000..8a56c51cb
--- /dev/null
+++ b/tools/rfc822parse.h
@@ -0,0 +1,81 @@
+/* rfc822parse.h - Simple mail and MIME parser
+ * Copyright (C) 1999 Werner Koch, Duesseldorf
+ * Copyright (C) 2003, g10 Code GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#ifndef RFC822PARSE_H
+#define RFC822PARSE_H
+
+struct rfc822parse_context;
+typedef struct rfc822parse_context *rfc822parse_t;
+
+typedef enum
+ {
+ RFC822PARSE_OPEN = 1,
+ RFC822PARSE_CLOSE,
+ RFC822PARSE_CANCEL,
+ RFC822PARSE_T2BODY,
+ RFC822PARSE_FINISH,
+ RFC822PARSE_RCVD_SEEN,
+ RFC822PARSE_LEVEL_DOWN,
+ RFC822PARSE_LEVEL_UP,
+ RFC822PARSE_BOUNDARY,
+ RFC822PARSE_LAST_BOUNDARY,
+ RFC822PARSE_BEGIN_HEADER,
+ RFC822PARSE_PREAMBLE,
+ RFC822PARSE_EPILOGUE
+ }
+rfc822parse_event_t;
+
+struct rfc822parse_field_context;
+typedef struct rfc822parse_field_context *rfc822parse_field_t;
+
+
+typedef int (*rfc822parse_cb_t) (void *opaque,
+ rfc822parse_event_t event,
+ rfc822parse_t msg);
+
+
+rfc822parse_t rfc822parse_open (rfc822parse_cb_t cb, void *opaque_value);
+
+void rfc822parse_close (rfc822parse_t msg);
+
+void rfc822parse_cancel (rfc822parse_t msg);
+int rfc822parse_finish (rfc822parse_t msg);
+
+int rfc822parse_insert (rfc822parse_t msg,
+ const unsigned char *line, size_t length);
+
+char *rfc822parse_get_field (rfc822parse_t msg, const char *name, int which,
+ size_t *valueoff);
+
+const char *rfc822parse_enum_header_lines (rfc822parse_t msg, void **context);
+
+rfc822parse_field_t rfc822parse_parse_field (rfc822parse_t msg,
+ const char *name,
+ int which);
+
+void rfc822parse_release_field (rfc822parse_field_t field);
+
+const char *rfc822parse_query_parameter (rfc822parse_field_t ctx,
+ const char *attr, int lower_value);
+
+const char *rfc822parse_query_media_type (rfc822parse_field_t ctx,
+ const char **subtype);
+
+#endif /*RFC822PARSE_H */
diff --git a/tools/ring-a-party b/tools/ring-a-party
deleted file mode 100755
index 01e0f3ff3..000000000
--- a/tools/ring-a-party
+++ /dev/null
@@ -1,114 +0,0 @@
-#!/bin/sh
-# ring-a-party - print a keyring suitable for a key signing party
-# Copyright (C) 2000, 2001 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 program 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.
-
-if [ $# -lt 1 ]; then
- echo "usage: ring-a-party keyring [headerline]" >&2
- exit 1
-fi
-
-keyring="$1"
-hdrline="$1"
-if [ $# -gt 1 ]; then
- hdrline="$2"
-fi
-
-if [ ! -f $keyring ]; then
- echo "ring-a-party: '$keyring': no such file" >&2
- exit 1
-fi
-
-echo "ring-a-party: output will be written to 'a.pub'" >&2
-
-
-gpg --dry-run --with-fingerprint --with-colons $keyring \
- | gawk -v "KEYRING=$hdrline" '
-BEGIN { FS=":"
- algos[1] = "RSA";
- algos[16] = "Elgamal";
- algos[17] = "DSA";
- any = 0;
- lines = -1;
- page = 0;
- now = strftime("%b %d %H:%M %Y");
- }
-END {
- if (any) myflush();
-}
-$1 == "pub" {
- if( any ) myflush();
- uidcount = 0;
- signencrypt = 0;
- uids[uidcount++] = $10;
- nbits = $3;
- keyid = substr($5,9);
- created = $6;
- expires = $7;
- algostr = mapalgo($4);
- if( $4 == 20 || $4 == 1 ) signencrypt = 1;
- any = 1;
- }
-$1 == "fpr" { fpr = $10 }
-$1 == "uid" { uids[uidcount++] = $10 }
-$1 == "sub" { if( $4 != 17 && $4 != 3 ) signencrypt=1 }
-
-function myflush()
-{
- # fixme: take lines to print here into account
- if( lines > 45 || lines == -1 ) {
- if( lines != -1 ) printf "\f";
- page++;
- printf "%s %-50.50s Page %d\n\n", now, KEYRING, page ;
- printf " Type Bits KeyID Created Expires Algorithm Use\n\n";
- lines = 1;
- }
- printf "[ ] pub %04d 0x%s %10s %10s %-10s %15s\n",
- nbits, keyid, created, expires == ""? "----------":expires, algostr,
- signencrypt == 1? "Sign & Encrypt":"Sign only";
- length(fpr) == 40 ? printfpr20( fpr ) : printfpr16( fpr );
- lnes += 2;
- for( i=0; i < uidcount; i++ ) {
- printf "( ) uid %s\n", uids[i];
- lines++;
- }
- printf "\n\n";
- lines += 2;
-}
-
-function mapalgo( no )
-{
- if( no in algos )
- return algos[no];
- return sprintf( "algoID=%ds", no );
-}
-
-
-function printfpr16( s )
-{
- printf " f16 Fingerprint16 =";
- for(i=0; i < 16; i++ ) {
- if( i == 8 ) printf " ";
- printf " %s", substr( s, i*2+1, 2 );
- }
- printf "\n"
-}
-
-function printfpr20( s )
-{
- printf " f20 Fingerprint20 =";
- for(i=0; i < 10; i++ ) {
- if( i == 5 ) printf " ";
- printf " %s", substr( s, i*4+1, 4 );
- }
- printf "\n"
-}
-
-' | tee a.pub | gpg --print-mds
diff --git a/tools/shmtest.c b/tools/shmtest.c
deleted file mode 100644
index f48340508..000000000
--- a/tools/shmtest.c
+++ /dev/null
@@ -1,201 +0,0 @@
-/* shmtest.c
- * Copyright (C) 1998, 1999, 2000, 2001 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 program 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.
- */
-
-
-#include <config.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <signal.h>
-#include <unistd.h>
-#ifdef HAVE_SYS_IPC_H
-#include <sys/types.h>
-#include <sys/ipc.h>
-#endif
-#ifdef HAVE_SYS_SHM_H
-#include <sys/shm.h>
-#endif
-#include "util.h"
-#include "ttyio.h"
-#include "i18n.h"
-
-#ifdef HAVE_DOSISH_SYSTEM
-int main( int argc, char **argv )
-{
- fprintf(stderr, "Sorry, not yet available for DOSish systems\n");
- exit(1);
-}
-#else
-
-static int serverpid = -1;
-
-static void
-my_usage(void)
-{
- fprintf(stderr, "usage: shmtest gpg-command-line\n");
- exit(1);
-}
-
-const char *
-strusage( int level )
-{
- return default_strusage(level);
-}
-
-static void
-i18n_init(void)
-{
-#ifdef ENABLE_NLS
- setlocale( LC_ALL, "" );
- bindtextdomain( PACKAGE, G10_LOCALEDIR );
- textdomain( PACKAGE );
-#endif
-}
-
-
-static void
-do_get_string( int mode, const char *keyword, byte *area, size_t areasize )
-{
- size_t n, len;
- char *p=NULL;
- int yes=0;
-
- n = area[0] << 8 | area[1];
- /* fixme: do some sanity checks here */
- if( mode == 1 )
- p = tty_get( keyword );
- else if( mode == 3 )
- p = tty_get_hidden( keyword );
- else
- yes = tty_get_answer_is_yes( keyword );
- if( p ) {
- len = strlen(p);
- memcpy( area+n+2, p, len );
- area[n] = len >> 8;
- area[n+1] = len;
- xfree(p);
- }
- else { /* bool */
- area[n] = 0;
- area[n+1] = 1;
- area[n+2] = yes;
- }
- area[3] = 1; /* we should better use a semaphore */
- kill( serverpid, SIGUSR1 );
-}
-
-
-
-int
-main(int argc, char **argv)
-{
- void *area = NULL;
- size_t areasize = 4096;
- int shm_id = -1;
- FILE *fp;
- char buf[200];
- char *p, *p2;
- size_t n;
- int i;
-
- log_set_name("shmtest");
- i18n_init();
-#ifndef USE_SHM_COPROCESSING
- log_info("SHM_COPRPOCESSING is not available\n");
-#else
- if( argc < 1 )
- my_usage();
-
- for(n=0,i=1; i < argc; i++ )
- n += strlen(argv[i]) + 1;
- p = xmalloc( 100 + n );
- strcpy( p, "../g10/gpg --status-fd 1 --run-as-shm-coprocess 0");
- for(i=1; i < argc; i++ ) {
- strcat(p, " " );
- strcat(p, argv[i] );
- }
-
- fp = popen( p, "r" );
- xfree( p );
- if( !fp )
- log_error("popen failed: %s\n", strerror(errno));
-
- while ( fgets (buf, sizeof (buf) - 1, fp ) != NULL ) {
- size_t len = strlen(buf);
- if( len >= 9 && !memcmp( buf, "[GNUPG:] ", 9 ) ) {
- int word=0;
- int is_info = 0, is_get = 0;
-
- for( p = strtok(buf+9, " \n"); p ; p = strtok(NULL, " \n")) {
- word++;
- if( word==1 && !strcmp(p,"SHM_INFO") ) {
- if( !area )
- is_info=1;
- else
- log_error("duplicate SHM_INFO ignored\n" );
- }
- else if( is_info && (p2 = strchr(p, '=' )) ) {
- int val;
- *p2++ = 0;
- val = atoi(p2); /* should be atou() for some values */
- if( !strcmp(p, "pv" ) ) {
- if( atoi(p2) != 1 )
- log_fatal("invalid protocol version %d\n", val );
- is_info = 2;
- }
- else if( !strcmp(p, "pid" ) )
- serverpid = val;
- else if( !strcmp(p, "shmid" ) )
- shm_id = val;
- }
- else if( word == 1 && !strcmp(p,"SHM_GET") )
- is_get = 1;
- else if( word == 1 && !strcmp(p,"SHM_GET_BOOL") )
- is_get = 2;
- else if( word == 1 && !strcmp(p,"SHM_GET_HIDDEN") )
- is_get = 3;
- else if( word == 2 && is_get ) {
- do_get_string( is_get, p, area, areasize );
- break;
- }
- else if( word == 1 )
- log_info("Status: %s\n", p);
- }
- if( is_info ) {
- if( is_info < 2 )
- log_fatal("SHM info without protocol version\n");
- if( serverpid == -1 )
- log_fatal("SHM info without server's pid\n");
- if( shm_id == -1 )
- log_fatal("SHM info without id\n");
- log_info("Shared memory info: server=%d shm_id=%d\n",
- serverpid, shm_id);
- area = shmat( shm_id, 0, 0 );
- if( area == (void*)-1 )
- log_fatal("attach to shared memory failed: %s\n",
- strerror(errno));
- }
- }
- else
- fputs (buf, stdout);
- }
-
-
- if( pclose(fp) )
- log_error("pclose failed\n");
-
- return 0;
-#endif
-}
-
-#endif
diff --git a/tools/signmany b/tools/signmany
deleted file mode 100644
index d628e7ba3..000000000
--- a/tools/signmany
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/sh
-
-if [ ! -f ./$1 ]; then
- echo "usage: signmany keyring" >&2
- exit 1
-fi
-
-
-ro="--trustdb-name=./tdb.tmp --no-default-keyring --secret-keyring /floppy/secring.gpg --keyring ./$1"
-
-kis=`gpg $ro --fast-list-mode --list-keys --with-colons \
- | awk -F: '$1=="pub" { print $5 }'`
-
-for k in $kis; do
- echo "Keyid: $k"
- answer=A
- while [ "$answer" = "A" ]; do
- gpg $ro --lock-never --no-interactive-selection --sign-key $k
- answer=""
- while [ "$answer" = "" ]; do
- read -p 'Okay, Again or Quit? (O/A/Q) '
- case "$REPLY" in
- o|O) answer=O ;;
- a|A) answer=A ;;
- q|Q) answer=Q ;;
- *) ;;
- esac
- done
- done
- [ "$answer" = "Q" ] && break
-done
-
-
diff --git a/tools/symcryptrun.c b/tools/symcryptrun.c
new file mode 100644
index 000000000..406cbb2a2
--- /dev/null
+++ b/tools/symcryptrun.c
@@ -0,0 +1,1073 @@
+/* symcryptrun.c - Tool to call simple symmetric encryption tools.
+ * 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.
+ */
+
+
+/* Sometimes simple encryption tools are already in use for a long
+ time and there is a desire to integrate them into the GnuPG
+ framework. The protocols and encryption methods might be
+ non-standard or not even properly documented, so that a
+ full-fledged encryption tool with an interface like gpg is not
+ doable. This simple wrapper program provides a solution: It
+ operates by calling the encryption/decryption module and providing
+ the passphrase for a key (or even the key directly) using the
+ standard pinentry mechanism through gpg-agent. */
+
+/* This program is invoked in the following way:
+
+ symcryptrun --class CLASS --program PROGRAM --keyfile KEYFILE \
+ [--decrypt | --encrypt]
+
+ For encryption, the plain text must be provided on STDIN, and the
+ ciphertext will be output to STDOUT. For decryption vice versa.
+
+ CLASS can currently only be "confucius".
+
+ PROGRAM must be the path to the crypto engine.
+
+ KEYFILE must contain the secret key, which may be protected by a
+ passphrase. The passphrase is retrieved via the pinentry program.
+
+
+ The GPG Agent _must_ be running before starting symcryptrun.
+
+ The possible exit status codes:
+
+ 0 Success
+ 1 Some error occured
+ 2 No valid passphrase was provided
+ 3 The operation was canceled by the user
+
+ Other classes may be added in the future. */
+
+#define SYMC_BAD_PASSPHRASE 2
+#define SYMC_CANCELED 3
+
+
+#include <config.h>
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <pty.h>
+#include <utmp.h>
+#include <ctype.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+#include <gpg-error.h>
+
+#define JNLIB_NEED_LOG_LOGV
+#include "i18n.h"
+#include "../common/util.h"
+#include "mkdtemp.h"
+
+/* FIXME: Bah. For spwq_secure_free. */
+#define SIMPLE_PWQUERY_IMPLEMENTATION 1
+#include "../common/simple-pwquery.h"
+
+
+/* Used by gcry for logging */
+static void
+my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
+{
+ /* translate the log levels */
+ switch (level)
+ {
+ case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
+ case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
+ case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
+ case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
+ case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
+ case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
+ case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
+ default: level = JNLIB_LOG_ERROR; break; }
+ log_logv (level, fmt, arg_ptr);
+}
+
+
+/* From simple-gettext.c. */
+
+/* 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;
+}
+
+
+/* Constants to identify the commands and options. */
+enum cmd_and_opt_values
+ {
+ aNull = 0,
+ oQuiet = 'q',
+ oVerbose = 'v',
+
+ oNoVerbose = 500,
+ oOptions,
+ oNoOptions,
+ oLogFile,
+ oHomedir,
+ oClass,
+ oProgram,
+ oKeyfile,
+ oDecrypt,
+ oEncrypt,
+ oInput
+ };
+
+
+/* The list of commands and options. */
+static ARGPARSE_OPTS opts[] =
+ {
+ { 301, NULL, 0, N_("@\nCommands:\n ") },
+
+ { oDecrypt, "decrypt", 0, N_("decryption modus") },
+ { oEncrypt, "encrypt", 0, N_("encryption modus") },
+
+ { 302, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oClass, "class", 2, N_("tool class (confucius)") },
+ { oProgram, "program", 2, N_("program filename") },
+
+ { oKeyfile, "keyfile", 2, N_("secret key file (required)") },
+ { oInput, "inputfile", 2, N_("input file name (default stdin)") },
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("quiet") },
+ { oLogFile, "log-file", 2, N_("use a log file for the server") },
+ { oOptions, "options" , 2, N_("|FILE|read options from FILE") },
+
+ /* Hidden options. */
+ { oNoVerbose, "no-verbose", 0, "@" },
+ { oHomedir, "homedir", 2, "@" },
+ { oNoOptions, "no-options", 0, "@" },/* shortcut for --options /dev/null */
+
+ {0}
+ };
+
+
+/* We keep all global options in the structure OPT. */
+struct
+{
+ int verbose; /* Verbosity level. */
+ int quiet; /* Be extra quiet. */
+ const char *homedir; /* Configuration directory name */
+
+ char *class;
+ char *program;
+ char *keyfile;
+ char *input;
+} opt;
+
+
+/* Print usage information and and provide strings for help. */
+static const char *
+my_strusage (int level)
+{
+ const char *p;
+
+ switch (level)
+ {
+ case 11: p = "symcryptrun (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
+ break;
+ case 1:
+ case 40: p = _("Usage: symcryptrun [options] (-h for help)");
+ break;
+ case 41:
+ p = _("Syntax: symcryptrun --class CLASS --program PROGRAM "
+ "--keyfile KEYFILE [options...] COMMAND [inputfile]\n"
+ "Call a simple symmetric encryption tool\n");
+ break;
+ case 31: p = "\nHome: "; break;
+ case 32: p = opt.homedir; break;
+ case 33: p = "\n"; break;
+
+ default: p = NULL; break;
+ }
+ return p;
+}
+
+
+/* Initialize the gettext system. */
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE_GT);
+#else
+# ifdef ENABLE_NLS
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE_GT, LOCALEDIR);
+ textdomain (PACKAGE_GT);
+# endif
+#endif
+}
+
+
+/* This is in the GNU C library in unistd.h. */
+
+#ifndef TEMP_FAILURE_RETRY
+/* Evaluate EXPRESSION, and repeat as long as it returns -1 with `errno'
+ set to EINTR. */
+
+# define TEMP_FAILURE_RETRY(expression) \
+ (__extension__ \
+ ({ long int __result; \
+ do __result = (long int) (expression); \
+ while (__result == -1L && errno == EINTR); \
+ __result; }))
+#endif
+
+
+/* Unlink a file, and shred it if SHRED is true. */
+int
+remove_file (char *name, int shred)
+{
+ if (!shred)
+ return unlink (name);
+ else
+ {
+ int status;
+ pid_t pid;
+
+ pid = fork ();
+ if (pid == 0)
+ {
+ /* Child. */
+
+ /* -f forces file to be writable, and -u unlinks it afterwards. */
+ char *args[] = { SHRED, "-uf", name, NULL };
+
+ execv (SHRED, args);
+ _exit (127);
+ }
+ else if (pid < 0)
+ {
+ /* Fork failed. */
+ status = -1;
+ }
+ else
+ {
+ /* Parent. */
+
+ if (TEMP_FAILURE_RETRY (waitpid (pid, &status, 0)) != pid)
+ status = -1;
+ }
+
+ if (!WIFEXITED (status))
+ {
+ log_error (_("%s on %s aborted with status %i\n"),
+ SHRED, name, status);
+ unlink (name);
+ return 1;
+ }
+ else if (WEXITSTATUS (status))
+ {
+ log_error (_("%s on %s failed with status %i\n"), SHRED, name,
+ WEXITSTATUS (status));
+ unlink (name);
+ return 1;
+ }
+
+ return 0;
+ }
+}
+
+
+/* Class Confucius.
+
+ "Don't worry that other people don't know you;
+ worry that you don't know other people." Analects--1.16. */
+
+/* Create temporary directory with mode 0700. Returns a dynamically
+ allocated string with the filename of the directory. */
+static char *
+confucius_mktmpdir (void)
+{
+ char *name;
+
+ name = strdup ("/tmp/gpg-XXXXXX");
+ if (!name || !mkdtemp (name))
+ {
+ log_error (_("can't create temporary directory `%s': %s\n"),
+ name?name:"", strerror (errno));
+ return NULL;
+ }
+
+ return name;
+}
+
+
+/* Buffer size for I/O operations. */
+#define CONFUCIUS_BUFSIZE 4096
+
+/* Buffer size for output lines. */
+#define CONFUCIUS_LINESIZE 4096
+
+
+/* Copy the file IN to OUT, either of which may be "-". If PLAIN is
+ true, and the copying fails, and OUT is not STDOUT, then shred the
+ file instead unlinking it. */
+static int
+confucius_copy_file (char *infile, char *outfile, int plain)
+{
+ FILE *in;
+ int in_is_stdin = 0;
+ FILE *out;
+ int out_is_stdout = 0;
+ char data[CONFUCIUS_BUFSIZE];
+ ssize_t data_len;
+
+ if (infile[0] == '-' && infile[1] == '\0')
+ {
+ /* FIXME: Is stdin in binary mode? */
+ in = stdin;
+ in_is_stdin = 1;
+ }
+ else
+ {
+ in = fopen (infile, "rb");
+ if (!in)
+ {
+ log_error (_("could not open %s for writing: %s\n"),
+ infile, strerror (errno));
+ return 1;
+ }
+ }
+
+ if (outfile[0] == '-' && outfile[1] == '\0')
+ {
+ /* FIXME: Is stdout in binary mode? */
+ out = stdout;
+ out_is_stdout = 1;
+ }
+ else
+ {
+ out = fopen (outfile, "wb");
+ if (!out)
+ {
+ log_error (_("could not open %s for writing: %s\n"),
+ infile, strerror (errno));
+ return 1;
+ }
+ }
+
+ /* Now copy the data. */
+ while ((data_len = fread (data, 1, sizeof (data), in)) > 0)
+ {
+ if (fwrite (data, 1, data_len, out) != data_len)
+ {
+ log_error (_("error writing to %s: %s\n"), outfile,
+ strerror (errno));
+ goto copy_err;
+ }
+ }
+ if (data_len < 0 || ferror (in))
+ {
+ log_error (_("error reading from %s: %s\n"), infile, strerror (errno));
+ goto copy_err;
+ }
+
+ /* Close IN if appropriate. */
+ if (!in_is_stdin && fclose (in) && ferror (in))
+ {
+ log_error (_("error closing %s: %s\n"), infile, strerror (errno));
+ goto copy_err;
+ }
+
+ /* Close OUT if appropriate. */
+ if (!out_is_stdout && fclose (out) && ferror (out))
+ {
+ log_error (_("error closing %s: %s\n"), infile, strerror (errno));
+ goto copy_err;
+ }
+
+ return 0;
+
+ copy_err:
+ if (!out_is_stdout)
+ remove_file (outfile, plain);
+
+ return 1;
+}
+
+
+/* Get a passphrase in secure storage (if possible). If AGAIN is
+ true, then this is a repeated attempt. If CANCELED is not a null
+ pointer, it will be set to true or false, depending on if the user
+ canceled the operation or not. On error (including cancelation), a
+ null pointer is returned. The passphrase must be deallocated with
+ confucius_drop_pass. CACHEID is the ID to be used for passphrase
+ caching and can be NULL to disable caching. */
+char *
+confucius_get_pass (const char *cacheid, int again, int *canceled)
+{
+ int err;
+ char *pw;
+#ifdef HAVE_LANGINFO_CODESET
+ char *orig_codeset = NULL;
+#endif
+
+ if (canceled)
+ *canceled = 0;
+
+#ifdef ENABLE_NLS
+ /* The Assuan agent protocol requires us to transmit utf-8 strings */
+ orig_codeset = bind_textdomain_codeset (PACKAGE_GT, NULL);
+#ifdef HAVE_LANGINFO_CODESET
+ if (!orig_codeset)
+ orig_codeset = nl_langinfo (CODESET);
+#endif
+ if (orig_codeset && !strcmp (orig_codeset, "UTF-8"))
+ orig_codeset = NULL;
+ if (orig_codeset)
+ {
+ /* We only switch when we are able to restore the codeset later. */
+ orig_codeset = xstrdup (orig_codeset);
+ if (!bind_textdomain_codeset (PACKAGE_GT, "utf-8"))
+ orig_codeset = NULL;
+ }
+#endif
+
+ pw = simple_pwquery (cacheid,
+ again ? _("does not match - try again"):NULL,
+ _("Passphrase:"), NULL, &err);
+
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ {
+ bind_textdomain_codeset (PACKAGE_GT, orig_codeset);
+ xfree (orig_codeset);
+ }
+#endif
+
+ if (!pw)
+ {
+ if (err)
+ log_error (_("error while asking for the passphrase: %s\n"),
+ gpg_strerror (err));
+ else
+ {
+ log_info (_("cancelled\n"));
+ if (canceled)
+ *canceled = 1;
+ }
+ }
+
+ return pw;
+}
+
+
+/* Drop a passphrase retrieved with confucius_get_pass. */
+void
+confucius_drop_pass (char *pass)
+{
+ if (pass)
+ spwq_secure_free (pass);
+}
+
+
+/* Run a confucius crypto engine. If MODE is oEncrypt, encryption is
+ requested. If it is oDecrypt, decryption is requested. INFILE and
+ OUTFILE are the temporary files used in the process. */
+int
+confucius_process (int mode, char *infile, char *outfile,
+ int argc, char *argv[])
+{
+ char **args;
+ int cstderr[2];
+ int master;
+ int slave;
+ int res;
+ pid_t pid;
+ pid_t wpid;
+ int tries = 0;
+ char cacheid[40];
+
+ signal (SIGPIPE, SIG_IGN);
+
+ if (!opt.program)
+ {
+ log_error (_("no --program option provided\n"));
+ return 1;
+ }
+
+ if (mode != oDecrypt && mode != oEncrypt)
+ {
+ log_error (_("only --decrypt and --encrypt are supported\n"));
+ return 1;
+ }
+
+ if (!opt.keyfile)
+ {
+ log_error (_("no --keyfile option provided\n"));
+ return 1;
+ }
+
+ /* Generate a hash from the keyfile name for caching. */
+ snprintf (cacheid, sizeof (cacheid), "confucius:%lu",
+ hash_string (opt.keyfile));
+ cacheid[sizeof (cacheid) - 1] = '\0';
+ args = malloc (sizeof (char *) * (10 + argc));
+ if (!args)
+ {
+ log_error (_("cannot allocate args vector\n"));
+ return 1;
+ }
+ args[0] = opt.program;
+ args[1] = (mode == oEncrypt) ? "-m1" : "-m2";
+ args[2] = "-q";
+ args[3] = infile;
+ args[4] = "-z";
+ args[5] = outfile;
+ args[6] = "-s";
+ args[7] = opt.keyfile;
+ args[8] = (mode == oEncrypt) ? "-af" : "-f";
+ args[9 + argc] = NULL;
+ while (argc--)
+ args[9 + argc] = argv[argc];
+
+ if (pipe (cstderr) < 0)
+ {
+ log_error (_("could not create pipe: %s\n"), strerror (errno));
+ free (args);
+ return 1;
+ }
+
+ if (openpty (&master, &slave, NULL, NULL, NULL) == -1)
+ {
+ log_error (_("could not create pty: %s\n"), strerror (errno));
+ close (cstderr[0]);
+ close (cstderr[1]);
+ free (args);
+ return -1;
+ }
+
+ /* We don't want to deal with the worst case scenarios. */
+ assert (master > 2);
+ assert (slave > 2);
+ assert (cstderr[0] > 2);
+ assert (cstderr[1] > 2);
+
+ pid = fork ();
+ if (pid < 0)
+ {
+ log_error (_("could not fork: %s\n"), strerror (errno));
+ close (master);
+ close (slave);
+ close (cstderr[0]);
+ close (cstderr[1]);
+ free (args);
+ return 1;
+ }
+ else if (pid == 0)
+ {
+ /* Child. */
+
+ /* Close the parent ends. */
+ close (master);
+ close (cstderr[0]);
+
+ /* Change controlling terminal. */
+ if (login_tty (slave))
+ {
+ /* It's too early to output a debug message. */
+ _exit (1);
+ }
+
+ dup2 (cstderr[1], 2);
+ close (cstderr[1]);
+
+ /* Now kick off the engine program. */
+ execv (opt.program, args);
+ log_error (_("execv failed: %s\n"), strerror (errno));
+ _exit (1);
+ }
+ else
+ {
+ /* Parent. */
+ char buffer[CONFUCIUS_LINESIZE];
+ int buffer_len = 0;
+ fd_set fds;
+ int slave_closed = 0;
+ int stderr_closed = 0;
+
+ close (slave);
+ close (cstderr[1]);
+ free (args);
+
+ /* Listen on the output FDs. */
+ do
+ {
+ FD_ZERO (&fds);
+
+ if (!slave_closed)
+ FD_SET (master, &fds);
+ if (!stderr_closed)
+ FD_SET (cstderr[0], &fds);
+
+ res = select (FD_SETSIZE, &fds, NULL, NULL, NULL);
+ if (res < 0)
+ {
+ log_error (_("select failed: %s\n"), strerror (errno));
+
+ kill (pid, SIGTERM);
+ close (master);
+ close (cstderr[0]);
+ return 1;
+ }
+
+ if (FD_ISSET (cstderr[0], &fds))
+ {
+ /* We got some output on stderr. This is just passed
+ through via the logging facility. */
+
+ res = read (cstderr[0], &buffer[buffer_len],
+ sizeof (buffer) - buffer_len - 1);
+ if (res < 0)
+ {
+ log_error (_("read failed: %s\n"), strerror (errno));
+
+ kill (pid, SIGTERM);
+ close (master);
+ close (cstderr[0]);
+ return 1;
+ }
+ else
+ {
+ char *newline;
+
+ buffer_len += res;
+ for (;;)
+ {
+ buffer[buffer_len] = '\0';
+ newline = strchr (buffer, '\n');
+ if (newline)
+ {
+ *newline = '\0';
+ log_error ("%s\n", buffer);
+ buffer_len -= newline + 1 - buffer;
+ memmove (buffer, newline + 1, buffer_len);
+ }
+ else if (buffer_len == sizeof (buffer) - 1)
+ {
+ /* Overflow. */
+ log_error ("%s\n", buffer);
+ buffer_len = 0;
+ }
+ else
+ break;
+ }
+
+ if (res == 0)
+ stderr_closed = 1;
+ }
+ }
+ else if (FD_ISSET (master, &fds))
+ {
+ char data[512];
+
+ res = read (master, data, sizeof (data));
+ if (res < 0)
+ {
+ if (errno == EIO)
+ {
+ /* Slave-side close leads to readable fd and
+ EIO. */
+ slave_closed = 1;
+ }
+ else
+ {
+ log_error (_("pty read failed: %s\n"), strerror (errno));
+
+ kill (pid, SIGTERM);
+ close (master);
+ close (cstderr[0]);
+ return 1;
+ }
+ }
+ else if (res == 0)
+ /* This never seems to be what happens on slave-side
+ close. */
+ slave_closed = 1;
+ else
+ {
+ /* Check for password prompt. */
+ if (data[res - 1] == ':')
+ {
+ char *pass;
+ int canceled;
+
+ /* If this is not the first attempt, the
+ passphrase seems to be wrong, so clear the
+ cache. */
+ if (tries)
+ simple_pwclear (cacheid);
+
+ pass = confucius_get_pass (cacheid,
+ tries ? 1 : 0, &canceled);
+ if (!pass)
+ {
+ kill (pid, SIGTERM);
+ close (master);
+ close (cstderr[0]);
+ return canceled ? SYMC_CANCELED : 1;
+ }
+ write (master, pass, strlen (pass));
+ write (master, "\n", 1);
+ confucius_drop_pass (pass);
+
+ tries++;
+ }
+ }
+ }
+ }
+ while (!stderr_closed || !slave_closed);
+
+ close (master);
+ close (cstderr[0]);
+
+ wpid = waitpid (pid, &res, 0);
+ if (wpid < 0)
+ {
+ log_error (_("waitpid failed: %s\n"), strerror (errno));
+
+ kill (pid, SIGTERM);
+ /* State of cached password is unclear. Just remove it. */
+ simple_pwclear (cacheid);
+ return 1;
+ }
+ else
+ {
+ /* Shouldn't happen, as we don't use WNOHANG. */
+ assert (wpid != 0);
+
+ if (!WIFEXITED (res))
+ {
+ log_error (_("child aborted with status %i\n"), res);
+
+ /* State of cached password is unclear. Just remove it. */
+ simple_pwclear (cacheid);
+
+ return 1;
+ }
+
+ if (WEXITSTATUS (res))
+ {
+ /* The passphrase was wrong. Remove it from the cache. */
+ simple_pwclear (cacheid);
+
+ /* We probably exceeded our number of attempts at guessing
+ the password. */
+ if (tries >= 3)
+ return SYMC_BAD_PASSPHRASE;
+ else
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ /* Not reached. */
+}
+
+
+/* Class confucius main program. If MODE is oEncrypt, encryption is
+ requested. If it is oDecrypt, decryption is requested. The other
+ parameters are taken from the global option data. */
+int
+confucius_main (int mode, int argc, char *argv[])
+{
+ int res;
+ char *tmpdir;
+ char *infile;
+ int infile_from_stdin = 0;
+ char *outfile;
+
+ tmpdir = confucius_mktmpdir ();
+ if (!tmpdir)
+ return 1;
+
+ if (opt.input && !(opt.input[0] == '-' && opt.input[1] == '\0'))
+ infile = xstrdup (opt.input);
+ else
+ {
+ infile_from_stdin = 1;
+
+ /* TMPDIR + "/" + "in" + "\0". */
+ infile = malloc (strlen (tmpdir) + 1 + 2 + 1);
+ if (!infile)
+ {
+ log_error (_("cannot allocate infile string: %s\n"),
+ strerror (errno));
+ rmdir (tmpdir);
+ return 1;
+ }
+ strcpy (infile, tmpdir);
+ strcat (infile, "/in");
+ }
+
+ /* TMPDIR + "/" + "out" + "\0". */
+ outfile = malloc (strlen (tmpdir) + 1 + 3 + 1);
+ if (!outfile)
+ {
+ log_error (_("cannot allocate outfile string: %s\n"), strerror (errno));
+ free (infile);
+ rmdir (tmpdir);
+ return 1;
+ }
+ strcpy (outfile, tmpdir);
+ strcat (outfile, "/out");
+
+ if (infile_from_stdin)
+ {
+ /* Create INFILE and fill it with content. */
+ res = confucius_copy_file ("-", infile, mode == oEncrypt);
+ if (res)
+ {
+ free (outfile);
+ free (infile);
+ rmdir (tmpdir);
+ return res;
+ }
+ }
+
+ /* Run the engine and thus create the output file, handling
+ passphrase retrieval. */
+ res = confucius_process (mode, infile, outfile, argc, argv);
+ if (res)
+ {
+ remove_file (outfile, mode == oDecrypt);
+ if (infile_from_stdin)
+ remove_file (infile, mode == oEncrypt);
+ free (outfile);
+ free (infile);
+ rmdir (tmpdir);
+ return res;
+ }
+
+ /* Dump the output file to stdout. */
+ res = confucius_copy_file (outfile, "-", mode == oDecrypt);
+ if (res)
+ {
+ remove_file (outfile, mode == oDecrypt);
+ if (infile_from_stdin)
+ remove_file (infile, mode == oEncrypt);
+ free (outfile);
+ free (infile);
+ rmdir (tmpdir);
+ return res;
+ }
+
+ remove_file (outfile, mode == oDecrypt);
+ if (infile_from_stdin)
+ remove_file (infile, mode == oEncrypt);
+ free (outfile);
+ free (infile);
+ rmdir (tmpdir);
+ return 0;
+}
+
+
+/* symcryptrun's entry point. */
+int
+main (int argc, char **argv)
+{
+ ARGPARSE_ARGS pargs;
+ int orig_argc;
+ char **orig_argv;
+ FILE *configfp = NULL;
+ char *configname = NULL;
+ unsigned configlineno;
+ int mode = 0;
+ int res;
+ char *logfile = NULL;
+ int default_config = 1;
+
+ set_strusage (my_strusage);
+ log_set_prefix ("symcryptrun", 1);
+
+ /* Try to auto set the character set. */
+ set_native_charset (NULL);
+
+ i18n_init();
+
+ opt.homedir = default_homedir ();
+
+ /* Check whether we have a config file given on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
+ while (arg_parse( &pargs, opts))
+ {
+ if (pargs.r_opt == oOptions)
+ { /* Yes there is one, so we do not try the default one, but
+ read the option file when it is encountered at the
+ commandline */
+ default_config = 0;
+ }
+ else if (pargs.r_opt == oNoOptions)
+ default_config = 0; /* --no-options */
+ else if (pargs.r_opt == oHomedir)
+ opt.homedir = pargs.r.ret_str;
+ }
+
+ if (default_config)
+ configname = make_filename (opt.homedir, "symcryptrun.conf", NULL );
+
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ next_pass:
+ if (configname)
+ {
+ configlineno = 0;
+ configfp = fopen (configname, "r");
+ if (!configfp)
+ {
+ if (!default_config)
+ {
+ log_error (_("option file `%s': %s\n"),
+ configname, strerror(errno) );
+ exit(1);
+ }
+ xfree (configname);
+ configname = NULL;
+ }
+ default_config = 0;
+ }
+
+ /* Parse the command line. */
+ while (optfile_parse (configfp, configname, &configlineno, &pargs, opts))
+ {
+ switch (pargs.r_opt)
+ {
+ case oDecrypt: mode = oDecrypt; break;
+ case oEncrypt: mode = oEncrypt; break;
+
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: opt.verbose++; break;
+ case oNoVerbose: opt.verbose = 0; break;
+
+ case oClass: opt.class = pargs.r.ret_str; break;
+ case oProgram: opt.program = pargs.r.ret_str; break;
+ case oKeyfile: opt.keyfile = pargs.r.ret_str; break;
+ case oInput: opt.input = pargs.r.ret_str; break;
+
+ case oLogFile: logfile = pargs.r.ret_str; break;
+
+ case oOptions:
+ /* Config files may not be nested (silently ignore them) */
+ if (!configfp)
+ {
+ xfree(configname);
+ configname = xstrdup(pargs.r.ret_str);
+ goto next_pass;
+ }
+ break;
+ case oNoOptions: break; /* no-options */
+ case oHomedir: /* Ignore this option here. */; break;
+
+ default : pargs.err = configfp? 1:2; break;
+ }
+ }
+ if (configfp)
+ {
+ fclose( configfp );
+ configfp = NULL;
+ configname = NULL;
+ goto next_pass;
+ }
+ xfree (configname);
+ configname = NULL;
+
+ if (!mode)
+ log_error (_("either %s or %s must be given\n"),
+ "--decrypt", "--encrypt");
+
+ if (log_get_errorcount (0))
+ exit (1);
+
+ if (logfile)
+ log_set_file (logfile);
+
+ gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
+ if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
+ {
+ log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
+ NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
+ }
+ gcry_set_log_handler (my_gcry_logger, NULL);
+ gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
+
+ if (!opt.class)
+ {
+ log_error (_("no class provided\n"));
+ res = 1;
+ }
+ else if (!strcmp (opt.class, "confucius"))
+ res = confucius_main (mode, argc, argv);
+ else
+ {
+ log_error (_("class %s is not supported\n"), opt.class);
+ res = 1;
+ }
+
+ return res;
+}
diff --git a/tools/watchgnupg.c b/tools/watchgnupg.c
new file mode 100644
index 000000000..051ca50fe
--- /dev/null
+++ b/tools/watchgnupg.c
@@ -0,0 +1,402 @@
+/* watchgnupg.c - Socket server for GnuPG logs
+ * Copyright (C) 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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <time.h>
+
+#define PGM "watchgnupg"
+
+/* Allow for a standalone build. */
+#ifdef VERSION
+#define MYVERSION_LINE PGM " (GnuPG) " VERSION
+#define BUGREPORT_LINE "\nReport bugs to <[email protected]>.\n"
+#else
+#define MYVERSION_LINE PGM
+#define BUGREPORT_LINE ""
+#endif
+
+#ifndef PF_LOCAL
+# ifdef PF_UNIX
+# define PF_LOCAL PF_UNIX
+# else
+# define PF_LOCAL AF_UNIX
+# endif
+# ifndef AF_LOCAL
+# define AF_LOCAL AF_UNIX
+# endif
+#endif
+
+
+static int verbose;
+
+
+static void
+die (const char *format, ...)
+{
+ va_list arg_ptr;
+
+ fflush (stdout);
+ fprintf (stderr, "%s: ", PGM);
+
+ va_start (arg_ptr, format);
+ vfprintf (stderr, format, arg_ptr);
+ va_end (arg_ptr);
+ putc ('\n', stderr);
+
+ exit (1);
+}
+
+
+/* static void */
+/* err (const char *format, ...) */
+/* { */
+/* va_list arg_ptr; */
+
+/* fflush (stdout); */
+/* fprintf (stderr, "%s: ", PGM); */
+
+/* va_start (arg_ptr, format); */
+/* vfprintf (stderr, format, arg_ptr); */
+/* va_end (arg_ptr); */
+/* putc ('\n', stderr); */
+/* } */
+
+static void *
+xmalloc (size_t n)
+{
+ void *p = malloc (n);
+ if (!p)
+ die ("out of core");
+ return p;
+}
+
+static void *
+xcalloc (size_t n, size_t m)
+{
+ void *p = calloc (n, m);
+ if (!p)
+ die ("out of core");
+ return p;
+}
+
+static void *
+xrealloc (void *old, size_t n)
+{
+ void *p = realloc (old, n);
+ if (!p)
+ die ("out of core");
+ return p;
+}
+
+
+struct client_s {
+ struct client_s *next;
+ int fd;
+ size_t size; /* Allocated size of buffer. */
+ size_t len; /* Current length of buffer. */
+ unsigned char *buffer; /* Buffer to with data already read. */
+
+};
+typedef struct client_s *client_t;
+
+
+
+static void
+print_fd_and_time (int fd)
+{
+ struct tm *tp;
+ time_t atime = time (NULL);
+
+ tp = localtime (&atime);
+ printf ("%3d - %04d-%02d-%02d %02d:%02d:%02d ",
+ fd,
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec );
+}
+
+
+/* Print LINE for the client identified by C. Calling this function
+ witgh LINE set to NULL, will flush the internal buffer. */
+static void
+print_line (client_t c, const char *line)
+{
+ const char *s;
+ size_t n;
+
+ if (!line)
+ {
+ if (c->buffer && c->len)
+ {
+ print_fd_and_time (c->fd);
+ fwrite (c->buffer, c->len, 1, stdout);
+ putc ('\n', stdout);
+ c->len = 0;
+ }
+ return;
+ }
+
+ while ((s = strchr (line, '\n')))
+ {
+ print_fd_and_time (c->fd);
+ if (c->buffer && c->len)
+ {
+ fwrite (c->buffer, c->len, 1, stdout);
+ c->len = 0;
+ }
+ fwrite (line, s - line + 1, 1, stdout);
+ line = s + 1;
+ }
+ n = strlen (line);
+ if (n)
+ {
+ if (c->len + n >= c->size)
+ {
+ c->size += ((n + 255) & ~255);
+ c->buffer = (c->buffer
+ ? xrealloc (c->buffer, c->size)
+ : xmalloc (c->size));
+ }
+ memcpy (c->buffer + c->len, line, n);
+ c->len += n;
+ }
+}
+
+
+static void
+print_version (int with_help)
+{
+ fputs (MYVERSION_LINE "\n"
+ "Copyright (C) 2004 Free Software Foundation, Inc.\n"
+ "This program comes with ABSOLUTELY NO WARRANTY.\n"
+ "This is free software, and you are welcome to redistribute it\n"
+ "under certain conditions. See the file COPYING for details.\n",
+ stdout);
+
+ if (with_help)
+ fputs ("\n"
+ "Usage: " PGM " [OPTIONS] SOCKETNAME\n"
+ "Open the local socket SOCKETNAME and display log messages\n"
+ "\n"
+ " --force delete an already existing socket file\n"
+ " --verbose enable extra informational output\n"
+ " --version print version of the program and exit\n"
+ " --help display this help and exit\n"
+ BUGREPORT_LINE, stdout );
+
+ exit (0);
+}
+
+int
+main (int argc, char **argv)
+{
+ int last_argc = -1;
+ int force = 0;
+
+ struct sockaddr_un srvr_addr;
+ socklen_t addrlen;
+ int server;
+ int flags;
+ client_t client_list = NULL;
+
+ if (argc)
+ {
+ argc--; argv++;
+ }
+ while (argc && last_argc != argc )
+ {
+ last_argc = argc;
+ if (!strcmp (*argv, "--"))
+ {
+ argc--; argv++;
+ break;
+ }
+ else if (!strcmp (*argv, "--version"))
+ print_version (0);
+ else if (!strcmp (*argv, "--help"))
+ print_version (1);
+ else if (!strcmp (*argv, "--verbose"))
+ {
+ verbose = 1;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--force"))
+ {
+ force = 1;
+ argc--; argv++;
+ }
+ }
+
+ if (argc != 1)
+ {
+ fprintf (stderr, "usage: " PGM " socketname\n");
+ exit (1);
+ }
+
+
+ if (verbose)
+ fprintf (stderr, "opening socket `%s'\n", *argv);
+
+ setvbuf (stdout, NULL, _IOLBF, 0);
+
+ server = socket (PF_LOCAL, SOCK_STREAM, 0);
+ if (server == -1)
+ die ("socket() failed: %s\n", strerror (errno));
+
+ /* We better set the listening socket to non-blocking so that we
+ don't get bitten by race conditions in accept. The should not
+ happen for Unix Domain sockets but well, shit happens. */
+ flags = fcntl (server, F_GETFL, 0);
+ if (flags == -1)
+ die ("fcntl (F_GETFL) failed: %s\n", strerror (errno));
+ if ( fcntl (server, F_SETFL, (flags | O_NONBLOCK)) == -1)
+ die ("fcntl (F_SETFL) failed: %s\n", strerror (errno));
+
+
+ memset (&srvr_addr, 0, sizeof srvr_addr);
+ srvr_addr.sun_family = AF_LOCAL;
+ strncpy (srvr_addr.sun_path, *argv, sizeof (srvr_addr.sun_path) - 1);
+ srvr_addr.sun_path[sizeof (srvr_addr.sun_path) - 1] = 0;
+ addrlen = (offsetof (struct sockaddr_un, sun_path)
+ + strlen (srvr_addr.sun_path) + 1);
+
+
+ again:
+ if (bind (server, (struct sockaddr *) &srvr_addr, addrlen))
+ {
+ if (errno == EADDRINUSE && force)
+ {
+ force = 0;
+ remove (srvr_addr.sun_path);
+ goto again;
+ }
+ die ("bind to `%s' failed: %s\n", *argv, strerror (errno));
+ }
+
+ if (listen (server, 5))
+ die ("listen failed: %s\n", strerror (errno));
+
+ for (;;)
+ {
+ fd_set rfds;
+ int max_fd;
+ client_t client;
+
+ /* Usually we don't have that many connections, thus it is okay
+ to set them allways from scratch and don't maintain an active
+ fd_set. */
+ FD_ZERO (&rfds);
+ FD_SET (server, &rfds);
+ max_fd = server;
+ for (client = client_list; client; client = client->next)
+ if (client->fd != -1)
+ {
+ FD_SET (client->fd, &rfds);
+ if (client->fd > max_fd)
+ max_fd = client->fd;
+ }
+
+ if (select (max_fd + 1, &rfds, NULL, NULL, NULL) <= 0)
+ continue; /* Ignore any errors. */
+
+ if (FD_ISSET (server, &rfds)) /* New connection. */
+ {
+ struct sockaddr_un clnt_addr;
+ int fd;
+
+ addrlen = sizeof clnt_addr;
+ fd = accept (server, (struct sockaddr *) &clnt_addr, &addrlen);
+ if (fd == -1)
+ {
+ printf ("[accepting connection failed: %s]\n", strerror (errno));
+ }
+ else if (fd >= FD_SETSIZE)
+ {
+ close (fd);
+ printf ("[connection request denied: too many connections]\n");
+ }
+ else
+ {
+ for (client = client_list; client && client->fd != -1;
+ client = client->next)
+ ;
+ if (!client)
+ {
+ client = xcalloc (1, sizeof *client);
+ client->next = client_list;
+ client_list = client;
+ }
+ client->fd = fd;
+ printf ("[client at fd %d connected]\n", client->fd);
+ }
+ }
+ for (client = client_list; client; client = client->next)
+ if (client->fd != -1 && FD_ISSET (client->fd, &rfds))
+ {
+ char line[256];
+ int n;
+
+ n = read (client->fd, line, sizeof line - 1);
+ if (n < 0)
+ {
+ int save_errno = errno;
+ print_line (client, NULL); /* flush */
+ printf ("[client at fd %d read error: %s]\n",
+ client->fd, strerror (save_errno));
+ close (client->fd);
+ client->fd = -1;
+ }
+ else if (!n)
+ {
+ print_line (client, NULL); /* flush */
+ close (client->fd);
+ printf ("[client at fd %d disconnected]\n", client->fd);
+ client->fd = -1;
+ }
+ else
+ {
+ line[n] = 0;
+ print_line (client, line);
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+Local Variables:
+compile-command: "gcc -Wall -g -o watchgnupg watchgnupg.c"
+End:
+*/